diff --git a/Modules/C3js/src/QmitkC3jsWidget.cpp b/Modules/C3js/src/QmitkC3jsWidget.cpp index 1bee6b8939..0b0cc00f4b 100644 --- a/Modules/C3js/src/QmitkC3jsWidget.cpp +++ b/Modules/C3js/src/QmitkC3jsWidget.cpp @@ -1,285 +1,285 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include "mitkImageTimeSelector.h" class QmitkC3jsWidget::Impl final { public: explicit Impl(QWidget* parent); ~Impl(); Impl(const Impl&) = delete; Impl& operator=(const Impl&) = delete; QWebChannel* GetWebChannel(); void ClearJavaScriptChart(); void initializeJavaScriptChart(); void callJavaScriptFuntion(QString command); - QmitkC3Data* GetC3Data() { return &m_c3Data; }; + QmitkC3Data* GetC3Data() { return &m_c3Data; } - mitk::Image::Pointer GetImage() { return m_Image; }; const - void SetImage(const mitk::Image::Pointer image) { m_Image = image; }; + mitk::Image::Pointer GetImage() const { return m_Image; } + void SetImage(const mitk::Image::Pointer image) { m_Image = image; } - mitk::PlanarFigure::ConstPointer GetPlanarFigure() { return m_PlanarFigure; }; const - void SetPlanarFigure(const mitk::PlanarFigure::ConstPointer planarFigure) { m_PlanarFigure = planarFigure; }; + mitk::PlanarFigure::ConstPointer GetPlanarFigure() const { return m_PlanarFigure; } + void SetPlanarFigure(const mitk::PlanarFigure::ConstPointer planarFigure) { m_PlanarFigure = planarFigure; } private: - QWidget* m_Parent; QWebChannel* m_WebChannel; QWebEngineView* m_WebEngineView; + QWidget* m_Parent; QmitkC3Data m_c3Data; /** * \brief Reference image. * * Holds the image to calculate an intensity profile. */ mitk::Image::Pointer m_Image; /** * \brief Pathelement. * * Holds a not closed planar figure to calculate an intensity profile. */ mitk::PlanarFigure::ConstPointer m_PlanarFigure; }; QmitkC3jsWidget::Impl::Impl(QWidget* parent) : m_WebChannel(new QWebChannel(parent)), m_WebEngineView(new QWebEngineView(parent)), m_Parent(parent) { //disable context menu for QWebEngineView m_WebEngineView->setContextMenuPolicy(Qt::NoContextMenu); //Set the webengineview to an initial empty page. The actual chart will be loaded once the data is calculated. m_WebEngineView->setUrl(QUrl(QStringLiteral("qrc:///C3js/empty.html"))); m_WebEngineView->page()->setWebChannel(m_WebChannel); connect( m_WebEngineView, SIGNAL( loadFinished(bool) ), parent, SLOT( OnLoadFinished(bool) ) ); auto layout = new QGridLayout(parent); layout->setMargin(0); layout->addWidget(m_WebEngineView); parent->setLayout(layout); } QmitkC3jsWidget::Impl::~Impl() { } QWebChannel* QmitkC3jsWidget::Impl::GetWebChannel() { return m_WebChannel; } QmitkC3jsWidget::QmitkC3jsWidget(QWidget* parent) : QWidget(parent), m_Impl(new Impl(this)) { m_Statistics = mitk::ImageStatisticsCalculator::StatisticsContainer::New(); } void QmitkC3jsWidget::Impl::callJavaScriptFuntion(QString command) { m_WebEngineView->page()->runJavaScript(command); } void QmitkC3jsWidget::Impl::ClearJavaScriptChart() { m_WebEngineView->setUrl(QUrl(QStringLiteral("qrc:///C3js/empty.html"))); } void QmitkC3jsWidget::Impl::initializeJavaScriptChart() { m_WebChannel->registerObject(QStringLiteral("initValues"), &m_c3Data); m_WebEngineView->load(QUrl(QStringLiteral("qrc:///C3js/QmitkC3jsWidget.html"))); } QmitkC3jsWidget::QmitkC3jsWidget(const QString& id, QObject* object, QWidget* parent) : QWidget(parent), m_Impl(new Impl(this)) { if (!id.isEmpty() && object != nullptr) m_Impl->GetWebChannel()->registerObject(id, object); m_Statistics = mitk::ImageStatisticsCalculator::StatisticsContainer::New(); } QmitkC3jsWidget::~QmitkC3jsWidget() { delete m_Impl; } -void QmitkC3jsWidget::OnLoadFinished(bool isLoadSuccessfull) +void QmitkC3jsWidget::OnLoadFinished(bool) { emit PageSuccessfullyLoaded(); } void QmitkC3jsWidget::TransformView(QString transformTo) { QString command = QString("transformView('" + transformTo + "')"); m_Impl->callJavaScriptFuntion(command); } void QmitkC3jsWidget::SendCommand(QString command) { m_Impl->callJavaScriptFuntion(command); } void QmitkC3jsWidget::SetAppearance(bool useLineChart, bool showSubChart) { this->m_Impl->GetC3Data()->SetAppearance(useLineChart, showSubChart); } // method to expose data to JavaScript by using properties void QmitkC3jsWidget::ComputeHistogram(HistogramType* histogram, bool useLineChart, bool showSubChart) { this->m_Impl->GetC3Data()->SetHistogram(histogram); SetAppearance(useLineChart, showSubChart); HistogramConstIteratorType startIt = this->m_Impl->GetC3Data()->GetHistogram()->End(); HistogramConstIteratorType endIt = this->m_Impl->GetC3Data()->GetHistogram()->End(); HistogramConstIteratorType it = this->m_Impl->GetC3Data()->GetHistogram()->Begin(); //Clear old data befor loading new data. this->m_Impl->GetC3Data()->ClearData(); unsigned int i = 0; bool firstValue = false; // removes frequencies of 0, which are outside the first and last bin for (; it != this->m_Impl->GetC3Data()->GetHistogram()->End(); ++it) { if (it.GetFrequency() > 0.0) { endIt = it; if (!firstValue) { firstValue = true; startIt = it; } } } ++endIt; // generating Lists of measurement and frequencies for (it = startIt; it != endIt; ++it, ++i) { QVariant frequency = QVariant::fromValue(it.GetFrequency()); QVariant measurement = it.GetMeasurementVector()[0]; this->m_Impl->GetC3Data()->GetYDataPointer()->insert(i, frequency); this->m_Impl->GetC3Data()->GetXDataPointer()->insert(i, measurement); } m_Impl->initializeJavaScriptChart(); } void QmitkC3jsWidget::ComputeIntensityProfile(unsigned int timeStep, bool computeStatistics) { this->ClearHistogram(); //m_Impl->GetC3Data()->ClearData(); //m_ParametricPath->Initialize(); if (m_Impl->GetPlanarFigure().IsNull()) { mitkThrow() << "PlanarFigure not set!"; } if (m_Impl->GetImage().IsNull()) { mitkThrow() << "Image not set!"; } mitk::Image::Pointer image; if (m_Impl->GetImage()->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(m_Impl->GetImage()); timeSelector->SetTimeNr(timeStep); timeSelector->Update(); image = timeSelector->GetOutput(); } else { image = m_Impl->GetImage(); } mitk::IntensityProfile::Pointer intensityProfile = mitk::ComputeIntensityProfile( image, const_cast(m_Impl->GetPlanarFigure().GetPointer())); //m_Frequency.clear(); //m_Measurement.clear(); int i = -1; mitk::IntensityProfile::ConstIterator end = intensityProfile->End(); for (mitk::IntensityProfile::ConstIterator it = intensityProfile->Begin(); it != end; ++it) { m_Impl->GetC3Data()->GetYDataPointer()->push_back(it.GetMeasurementVector()[0]); //m_Impl->GetC3Data()->GetFrequencyPointer()->push_back(50000); m_Impl->GetC3Data()->GetXDataPointer()->push_back(++i); } if (computeStatistics) { mitk::ComputeIntensityProfileStatistics(intensityProfile, m_Statistics); } m_Impl->initializeJavaScriptChart(); } void QmitkC3jsWidget::ClearHistogram() { m_Impl->GetC3Data()->ClearData(); m_Impl->ClearJavaScriptChart(); } mitk::Image::Pointer QmitkC3jsWidget::GetImage() const { return m_Impl->GetImage(); } void QmitkC3jsWidget::SetImage(const mitk::Image::Pointer image) { m_Impl->SetImage(image); } mitk::PlanarFigure::ConstPointer QmitkC3jsWidget::GetPlanarFigure() const { return m_Impl->GetPlanarFigure(); } void QmitkC3jsWidget::SetPlanarFigure(const mitk::PlanarFigure::ConstPointer planarFigure) { m_Impl->SetPlanarFigure(planarFigure); -} \ No newline at end of file +} diff --git a/Modules/CEST/autoload/IO/mitkCESTDICOMReaderService.h b/Modules/CEST/autoload/IO/mitkCESTDICOMReaderService.h index f547196574..1eee6804ea 100644 --- a/Modules/CEST/autoload/IO/mitkCESTDICOMReaderService.h +++ b/Modules/CEST/autoload/IO/mitkCESTDICOMReaderService.h @@ -1,49 +1,50 @@ /*=================================================================== 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 MITKCESTDICOMReaderService_H #define MITKCESTDICOMReaderService_H #include namespace mitk { /** Service wrapper that auto selects (using the mitk::DICOMFileReaderSelector) the best DICOMFileReader from the DICOMReader module and loads additional meta data for CEST data. */ class CESTDICOMReaderService : public BaseDICOMReaderService { public: CESTDICOMReaderService(); CESTDICOMReaderService(const std::string& description); /** Uses the BaseDICOMReaderService Read function and add extra steps for CEST meta data */ + using AbstractFileReader::Read; virtual std::vector > Read() override; protected: /** Returns the reader instance that should be used. The decision may be based * one the passed relevant file list.*/ virtual mitk::DICOMFileReader::Pointer GetReader(const mitk::StringList& relevantFiles) const override; private: virtual CESTDICOMReaderService* Clone() const override; }; } #endif // MITKDICOMSERIESREADERSERVICE_H diff --git a/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp b/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp index f706bf9cda..424174dadf 100644 --- a/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp +++ b/Modules/Classification/CLLibSVM/test/mitkLibSVMClassifierTest.cpp @@ -1,322 +1,314 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include "mitkIOUtil.h" #include "itkArray2D.h" #include #include #include #include #include #include #include //#include 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 MatrixDoubleType; typedef Eigen::Matrix 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 std::pair,Eigen::Matrix >convertCSVToMatrix(const std::string &path, char delimiter,double range, bool isXMatrix) { typename itk::CSVArray2DFileReader::Pointer fr = itk::CSVArray2DFileReader::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::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 trainMatrixX(percentRange,c); Eigen::Matrix testMatrixXPredict(maxrowrange-percentRange,c); for(unsigned int row = 0; row < percentRange; row++){ for(unsigned int col = 0; col < c; col++){ trainMatrixX(row,col) = p->GetData(row,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 { Eigen::Matrix trainLabelMatrixY(percentRange,c); Eigen::Matrix testMatrixYPredict(maxrowrange-percentRange,c); for(unsigned int row = 0; row < percentRange; row++){ for(unsigned int col = 0; col < c; col++){ trainLabelMatrixY(row,col) = p->GetData(row,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 Eigen::Matrix readCsvData(const std::string &path, char delimiter) { typename itk::CSVArray2DFileReader::Pointer fr = itk::CSVArray2DFileReader::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::Pointer p = fr->GetOutput(); unsigned int maxrowrange = p->GetMatrix().rows(); unsigned int maxcols = p->GetMatrix().cols(); Eigen::Matrix 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 void writeMatrixToCsv(Eigen::Matrix 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 matrixDouble; matrixDouble = convertCSVToMatrix(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 matrixInt; matrixInt = convertCSVToMatrix(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 (unsigned int i = 0; i < maxrows; i++) { if(classes(i, 0) == m_TestYPredict(i, 0)) - { - isYPredictVector = true; ++count; - } } MITK_INFO << 100*count/(double)(maxrows) << "%"; MITK_TEST_CONDITION(isEqual(m_TestYPredict,classes),"Expected vector and occured vector match."); } // Method of testing for assertions. template bool isEqual(Eigen::Matrix expected, Eigen::Matrix actual) { bool isSimilar = true; unsigned int mrow = expected.rows(); unsigned int mcol = expected.cols(); 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 bool isIntervall(Eigen::Matrix expected, Eigen::Matrix actual, double lowrange, double toprange) { bool isInIntervall = false; int count = 0; unsigned int rowRange = expected.rows(); unsigned int colRange = expected.cols(); 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 matrixDouble; matrixDouble = convertCSVToMatrix(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 matrixInt; matrixInt = convertCSVToMatrix(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 (unsigned int i = 0; i < maxrows; i++) { if (classes(i, 0) == m_TestYPredict(i, 0)) - { - isYPredictVector = true; ++count; - } } MITK_INFO << 100*count/(double)(maxrows) << "%"; MITK_TEST_CONDITION(isIntervall(m_TestYPredict,classes,75,100),"Testvalue is in range."); } void TestThreadedDecisionForest() { } }; -MITK_TEST_SUITE_REGISTRATION(mitkLibSVMClassifier) \ No newline at end of file +MITK_TEST_SUITE_REGISTRATION(mitkLibSVMClassifier) diff --git a/Modules/Classification/DataCollection/DataHolder/mitkDataCollection.h b/Modules/Classification/DataCollection/DataHolder/mitkDataCollection.h index a7529d84d2..50d9591777 100644 --- a/Modules/Classification/DataCollection/DataHolder/mitkDataCollection.h +++ b/Modules/Classification/DataCollection/DataHolder/mitkDataCollection.h @@ -1,288 +1,290 @@ /*=================================================================== 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. ===================================================================*/ -#pragma warning (disable : 4996) +#ifdef _MSC_VER +# pragma warning (disable : 4996) +#endif #ifndef mitkDataCollection_H_ #define mitkDataCollection_H_ //#include //#include //#include "itkObjectFactory.h" //#include "mitkCommon.h" #include #include #include #include /** * \brief DataCollection - Class to facilitate loading/accessing structured data * * Data is grouped into a collection that may contain further (sub) collections or images. * * Exemplary structure * * Collection (e.g. Patient) * | * |-- Sub-Collection1 (e.g. follow-up 1) * | | * | |-- DataItem (e.g. T1) * | |-- DataItem (e.g. T2) * | * |-- Sub-Collection2 (e.g. follow-up 2) * | | * | |-- DataItem (e.g. T1) * | |-- DataItem (e.g. T2) * */ namespace mitk { class MITKDATACOLLECTION_EXPORT DataCollection : public BaseData { public: mitkClassMacro(DataCollection, BaseData) itkFactorylessNewMacro(Self) itkCloneMacro(Self) // Needed methods from Basedata virtual void UpdateOutputInformation(); virtual void SetRequestedRegionToLargestPossibleRegion(); virtual bool RequestedRegionIsOutsideOfTheBufferedRegion(); virtual bool VerifyRequestedRegion(); virtual void SetRequestedRegion(const itk::DataObject *); void Init(std::string name); /** * @brief AddData Add a data item * @param data Images/Fibers/DataCollections * @param name name that allows identifying this data (e.g. a category T2, Segmentation , etc ...) * @param description * @return */ size_t AddData(DataObject::Pointer data, std::string name, std::string filePath = ""); /** * @brief SetName - Sets name of DataCollection * @param name */ void SetName(std::string name); std::string GetName() const; std::string GetDataFilePath(size_t index) const; /** * @brief NameToIndex - Get index from data item name * @param name * @return */ size_t NameToIndex(std::string name); /** * @brief IndexToName - Get name from index * @param index * @return */ std::string IndexToName(size_t index) const; /** * @brief HasElement - check if element with this name exists in collection * @param name * @return */ bool HasElement(std::string name); /** * @brief HasElement - check if element with this index exists in collection * @param index * @return */ bool HasElement(size_t index); /** * @brief Size - number of data items in collection * @return */ size_t Size() const; /** * @brief SetData - set/update data item by index * @param data * @param index */ void SetData(itk::DataObject::Pointer data, size_t index); /** * @brief SetData - set/update data item by name * @param data * @param name */ void SetData(itk::DataObject::Pointer data, std::string name); /** * @brief GetData Get original data by index * * To ensure a mitk::Image is returned use \ref mitk::GetMitkImage * * @param index * @return */ itk::DataObject::Pointer GetData(size_t index); /** * @brief GetData Get original data by name * * To ensure a mitk::Image is returned use \ref mitk::GetMitkImage * * @param name * @return */ itk::DataObject::Pointer GetData(std::string name); /** * @brief GetMitkImage - casts data to mitk::Image and returns it * * \note returns nullptr is object is no mitk::Image or itk::Image * * @param index * @return */ mitk::Image::Pointer GetMitkImage(size_t index); /** * @brief GetMitkImage - casts data to mitk::Image and returns it * * \note returns nullptr is object is no mitk::Image or itk::Image * * @param name * @return */ mitk::Image::Pointer GetMitkImage(std::string name); /** * @brief GetMitkImage - casts data to privided itk::Image pointer */ template ImageType GetItkImage(size_t index, ImageType* itkImage); /** * @brief GetMitkImage - casts data to privided itk::Image pointer */ template ImageType GetItkImage(std::string name, ImageType* itkImage); itk::DataObject::Pointer& operator[](size_t index); itk::DataObject::Pointer& operator[](std::string &name); /** * @brief SetNameForIndex - sets name for given data item by index * @param index * @param name */ void SetNameForIndex(size_t index, std::string &name); /** * @brief SetXMLFile - sets xml file to which data collection is saved */ void SetXMLFile(std::string absoluteXMlFile); /** * @brief SetXMLFile - gets xml file to which data collection is supposed to be saved */ std::string GetXMLFile(); /** * @brief SetParent - sets the parent collection * @param parent */ void SetParent(mitk::DataCollection* parent); /** * @brief GetParent - returns the parent collection if available else null is returned * @return */ mitk::DataCollection* GetParent(); /** * @brief RemoveIndex - removes element at index * @param index * @return */ bool RemoveIndex(size_t index); /** * @brief RemoveElement - removes element with name * @param name * @return */ bool RemoveElement(std::string& name); /** * @brief Clear - clears the data collection */ void Clear(); /** * @brief GetDataNode - returns data node containing data at index * @param index * @return */ mitk::DataNode::Pointer GetDataNode(size_t index); /** * @brief GetDataNode - returns data node containing data with name * @param name * @return */ mitk::DataNode::Pointer GetDataNode(std::string name); /** * @brief GetProbabilityMap - returns vectorimage generated out of images with names in the probabilityNamesVector * @param probabilityNamesVector * @return */ mitk::Image::Pointer GetProbabilityMap(std::vector probabilityNamesVector); protected: DataCollection(); virtual ~DataCollection(); private: // DATA std::string m_Name; std::vector m_DataVector; std::vector m_NameVector; std::vector m_FilePathVector; std::map m_DataNames; mitk::DataCollection * m_Parent; std::string m_XMLFile; // is only filled for the hightest layer when loading a data collection }; } // end namespace #endif diff --git a/Modules/Classification/DataCollection/ReaderWriter/mitkCollectionReader.cpp b/Modules/Classification/DataCollection/ReaderWriter/mitkCollectionReader.cpp index 4c365a7df0..0a4296f875 100644 --- a/Modules/Classification/DataCollection/ReaderWriter/mitkCollectionReader.cpp +++ b/Modules/Classification/DataCollection/ReaderWriter/mitkCollectionReader.cpp @@ -1,390 +1,392 @@ /*=================================================================== 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. ===================================================================*/ -#pragma warning (disable : 4996) +#ifdef _MSC_VER +# pragma warning (disable : 4996) +#endif #include "mitkCollectionReader.h" #include #include //#include #include //XML StateMachine Tags // Objects const std::string COLLECTION = "col"; const std::string SUBCOLLECTION = "subcol"; const std::string DATA = "data"; const std::string ITEM = "item"; // Properties const std::string NAME = "name"; const std::string ID = "id"; const std::string FILEPATH = "description"; const std::string LINK = "link"; static std::string GetName(std::string fileName,std::string suffix) { fileName = QFileInfo(QString::fromStdString(fileName)).fileName().toStdString(); return fileName.substr(0,fileName.length() -suffix.length()-9); // 8 = date length } static std::string GetDate(std::string fileName,std::string suffix) { fileName = QFileInfo(QString::fromStdString(fileName)).fileName().toStdString(); fileName = fileName.substr(fileName.length() - suffix.length()-8,8); // 8 = date length fileName.insert(6,"-"); fileName.insert(4,"-"); return fileName; } mitk::CollectionReader::CollectionReader() : m_Collection(nullptr), m_SubCollection(nullptr), m_DataItemCollection(nullptr), m_ColIgnore(false), m_ItemIgnore(false) { } mitk::CollectionReader::~CollectionReader() { this->Clear(); } /** * @brief Loads the xml file filename and generates the necessary instances. **/ mitk::DataCollection::Pointer mitk::CollectionReader::LoadCollection(const std::string& xmlFileName) { QDir fileName = QFileInfo(xmlFileName.c_str()).absoluteDir(); m_BaseDir = fileName.path().toStdString() + QDir::separator().toLatin1(); this->SetFileName(xmlFileName.c_str()); this->Parse(); if (m_Collection.IsNotNull()) m_Collection->SetXMLFile(xmlFileName); return m_Collection; } void mitk::CollectionReader::AddDataElementIds(std::vector dataElemetIds) { m_SelectedDataItemIds.insert( m_SelectedDataItemIds.end(), dataElemetIds.begin(), dataElemetIds.end() ); } void mitk::CollectionReader::AddSubColIds(std::vector subColIds) { m_SelectedSubColIds.insert( m_SelectedSubColIds.end(), subColIds.begin(), subColIds.end() ); } void mitk::CollectionReader::SetDataItemNames(std::vector itemNames) { m_SelectedDataItemNames = itemNames; } void mitk::CollectionReader::ClearDataElementIds() { m_SelectedDataItemIds.clear(); } void mitk::CollectionReader::ClearSubColIds() { m_SelectedSubColIds.clear(); } void mitk::CollectionReader::Clear() { m_DataItemCollection = nullptr; m_SubCollection = nullptr; m_Collection = nullptr; } mitk::DataCollection::Pointer mitk::CollectionReader::FolderToCollection(std::string folder, std::vector suffixes,std::vector seriesNames, bool allowGaps) { // Parse folder and look up all data, // after sanitation only fully available groups are included (that is all suffixes are found) FileListType fileList = SanitizeFileList(GenerateFileLists(folder, suffixes, allowGaps)); if (fileList.size() <= 0) return nullptr; DataCollection::Pointer collection = DataCollection::New(); collection->SetName(GetName(fileList.at(0).at(0),suffixes.at(0))); for (unsigned int k=0; k < fileList.at(0).size(); ++k) // all groups have the same amount of items, so looking at 0 is ok. { DataCollection::Pointer subCollection = DataCollection::New(); for (unsigned int i=0; i< suffixes.size(); ++i) { Image::Pointer image = IOUtil::LoadImage(fileList.at(i).at(k)); subCollection->AddData(image.GetPointer(),seriesNames.at(i), fileList.at(i).at(k)); } std::string sDate = GetDate(fileList.at(0).at(k),suffixes.at(0)); collection->AddData(subCollection.GetPointer(),sDate,"--"); } return collection; } void mitk::CollectionReader::StartElement(const char* elementName, const char **atts) { std::string name(elementName); if (name == COLLECTION) { m_Collection = DataCollection::New(); std::string colName = ReadXMLStringAttribut(NAME, atts); m_Collection->SetName(colName); } else if (name == SUBCOLLECTION) { m_ColIgnore = false; m_ItemIgnore = false; std::string subColName = ReadXMLStringAttribut(NAME, atts); std::string subColId = ReadXMLStringAttribut(ID, atts); if (m_SelectedSubColIds.size() > 0 && std::find(m_SelectedSubColIds.begin(), m_SelectedSubColIds.end(), subColId) == m_SelectedSubColIds.end() ) { // a) a selection list is provided AND b) the item is not in the list m_ColIgnore = true; return; } // Create subcollection m_SubCollection = DataCollection::New(); m_SubCollection->Init(subColName); } else if (name == DATA) { if (m_ColIgnore) return; std::string dataId = ReadXMLStringAttribut(ID, atts); if (m_SelectedDataItemIds.size() > 0 && std::find(m_SelectedDataItemIds.begin(), m_SelectedDataItemIds.end(), dataId) == m_SelectedDataItemIds.end() ) { // a) a selection list is provided AND b) the item is not in the list m_ItemIgnore = true; return; } m_ItemIgnore = false; std::string dataName = ReadXMLStringAttribut(NAME, atts); m_DataItemCollection = DataCollection::New(); m_DataItemCollection->Init(dataName); } else if (name == ITEM) { if (m_ColIgnore || m_ItemIgnore) return; std::string relativeItemLink = ReadXMLStringAttribut(LINK, atts); std::string itemLink = m_BaseDir + relativeItemLink; std::string itemName = ReadXMLStringAttribut(NAME, atts); // if item names are provided and name is not in list, do not load it if (m_SelectedDataItemNames.size() != 0 && std::find(m_SelectedDataItemNames.begin(), m_SelectedDataItemNames.end(), itemName) == m_SelectedDataItemNames.end() ) return; // Populate Sub-Collection Image::Pointer image = IOUtil::LoadImage(itemLink); if (image.IsNotNull()) m_DataItemCollection->AddData(image.GetPointer(),itemName,relativeItemLink); else MITK_ERROR << "File could not be loaded: " << itemLink << ". Wihtin Sub-Collection " << m_SubCollection->GetName() << ", within " << m_DataItemCollection->GetName() ; } else MITK_WARN<< "Malformed description ? -- unknown tag: " << name; } void mitk::CollectionReader::EndElement(const char* elementName) { std::string name(elementName); if (name == SUBCOLLECTION) { if (m_SubCollection.IsNull()) return; if (m_ColIgnore || m_SubCollection->Size() == 0) return; m_Collection->AddData(m_SubCollection.GetPointer(),m_SubCollection->GetName()); m_SubCollection = DataCollection::New(); } if (name == DATA) { if (m_DataItemCollection.IsNull()) return; if (m_DataItemCollection->Size() == 0) return; m_SubCollection->AddData(m_DataItemCollection.GetPointer(),m_DataItemCollection->GetName()); m_DataItemCollection = DataCollection::New(); } } std::string mitk::CollectionReader::ReadXMLStringAttribut(std::string name, const char** atts) { if (atts) { const char** attsIter = atts; while (*attsIter) { if (name == *attsIter) { attsIter++; return *attsIter; } attsIter++; attsIter++; } } return std::string(); } bool mitk::CollectionReader::ReadXMLBooleanAttribut(std::string name, const char** atts) { std::string s = ReadXMLStringAttribut(name, atts); std::transform(s.begin(), s.end(), s.begin(), ::toupper); if (s == "TRUE") return true; else return false; } int mitk::CollectionReader::ReadXMLIntegerAttribut(std::string name, const char** atts) { std::string s = ReadXMLStringAttribut(name, atts); return atoi(s.c_str()); } mitk::CollectionReader::FileListType mitk::CollectionReader::GenerateFileLists(std::string folder, std::vector suffixes, bool allowGaps) { FileListType fileList; QString qFolder = QString::fromStdString(folder); if (!QFileInfo(qFolder).isDir()) { MITK_ERROR << "Folder does not exist."; return fileList; } // Add vector for each suffix for (unsigned int i=0; i< suffixes.size(); ++i) { std::vector list; fileList.push_back(list); } // if gaps are allowed, file names are build up from reference file (first suffix) // else all lists a file, file by file with regular sorting of the files, // if one suffix has more/less images than the others loading is aborted if (allowGaps) { QDir parseDir; parseDir.setFilter(QDir::Files); parseDir.setPath(qFolder); QStringList filterMorph; filterMorph << "*" + QString::fromStdString( suffixes.at(0) ); parseDir.setNameFilters( filterMorph ); QFileInfoList qFileList = parseDir.entryInfoList(); // now populate lists with files names, non-existing files will be marked with an empty string for (int i = 0; i < qFileList.size(); ++i) { std::string baseFileName = qFileList.at(i).absoluteFilePath().toStdString(); fileList.at(0).push_back( baseFileName ); //check for different suffixes for (unsigned int suffNo=1; suffNo < suffixes.size(); ++suffNo) { std::string derivedFileName = baseFileName.substr(0,baseFileName.length() -suffixes.at(0).length()) + suffixes.at(suffNo); // checking if file exists if (QFileInfo(QString::fromStdString(derivedFileName)).isFile()) fileList.at(suffNo).push_back(derivedFileName); else fileList.at(suffNo).push_back(""); } } } else { int numberOfFiles=-1; for (unsigned int i=0; i< suffixes.size(); ++i) { QDir parseDir; parseDir.setFilter(QDir::Files); parseDir.setPath(qFolder); QStringList filterMorph; filterMorph << "*" + QString::fromStdString( suffixes.at(i) ); parseDir.setNameFilters( filterMorph ); QFileInfoList qFileList = parseDir.entryInfoList(); if (numberOfFiles == -1) numberOfFiles = qFileList.size(); if (numberOfFiles != qFileList.size() ) { MITK_ERROR << "Series contain different number of images. Loading aborting."; fileList.clear(); break; } for (int fileNo=0; fileNo indexRemoval; // Parse through all items and check for empty strings, if one occurs mark this index // for removal. int modalities = list.size(); int timeSteps = list.at(0).size(); MITK_INFO << "Modalities " << modalities; MITK_INFO << "TimeSteps " << timeSteps; if (timeSteps == 0) MITK_ERROR << "No files found. Fatal."; for (int listIndex = 0 ; listIndex < timeSteps; listIndex++) { for (int modalityIndex = 0 ; modalityIndex < modalities; modalityIndex++) { if (list.at(modalityIndex).at(listIndex) == "") { MITK_INFO << "Marked Index " << listIndex << " for removal."; indexRemoval.push_back(listIndex); break; } } } for (int listIndex = indexRemoval.size()-1 ; listIndex >= 0; --listIndex) { for (int i = 0 ; i < modalities; i++) { list.at(i).erase(list.at(i).begin()+indexRemoval.at(listIndex)) ; } } MITK_INFO << "Time Steps after sanitizing: " << list.at(0).size(); return list; } diff --git a/Modules/Classification/DataCollection/ReaderWriter/mitkCollectionWriter.cpp b/Modules/Classification/DataCollection/ReaderWriter/mitkCollectionWriter.cpp index 0bd5b170cb..626cc40e3c 100755 --- a/Modules/Classification/DataCollection/ReaderWriter/mitkCollectionWriter.cpp +++ b/Modules/Classification/DataCollection/ReaderWriter/mitkCollectionWriter.cpp @@ -1,418 +1,420 @@ /*=================================================================== 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. ===================================================================*/ -#pragma warning (disable : 4996) +#ifdef _MSC_VER +# pragma warning (disable : 4996) +#endif #include "mitkCollectionWriter.h" #include #include "mitkImageCast.h" #include "itkNrrdImageIO.h" #include "itkImageFileWriter.h" #include "mitkCoreObjectFactory.h" #include #include #include //XML StateMachine Tags // Objects const std::string COLLECTION = "col"; const std::string SUBCOLLECTION = "subcol"; const std::string DATA = "data"; const std::string ITEM = "item"; // Properties const std::string NAME = "name"; const std::string ID = "id"; const std::string FILEPATH = "filepath"; const std::string LINK = "link"; static std::string GetName(std::string fileName,std::string suffix, bool longName = false) { fileName = QFileInfo(QString::fromStdString(fileName)).fileName().toStdString(); if (longName) return fileName.substr(0,fileName.length() -suffix.length()-11); // 10 = date length else return fileName.substr(0,fileName.length() -suffix.length()-9); // 8 = date length } static std::string GetDate(std::string fileName,std::string suffix, bool longName = false) { fileName = QFileInfo(QString::fromStdString(fileName)).fileName().toStdString(); if (longName) fileName = fileName.substr(fileName.length() - suffix.length()-10,10); // 8 = date length else fileName = fileName.substr(fileName.length() - suffix.length()-8,8); // 8 = date length if (!longName) { fileName.insert(6,"-"); fileName.insert(4,"-"); } return fileName; } bool mitk::CollectionWriter::ExportCollectionToFolder(DataCollection *dataCollection, std::string xmlFile, std::vector filter) { // Quick and Dirty: Assumes three level DataCollection QDir fileName = QFileInfo(xmlFile.c_str()).absoluteDir(); std::string outputFolder = fileName.path().toStdString() + QDir::separator().toLatin1(); QDir baseFolder(outputFolder.c_str()); baseFolder.mkpath(outputFolder.c_str()); std::ofstream xmlFileStream; xmlFileStream.open (xmlFile.c_str()); xmlFileStream << " \n"; xmlFileStream << "<" << COLLECTION << " " << NAME << "=\"" << dataCollection->GetName() << "\" >\n"; unsigned int subColId = 0; unsigned int dataId = 0; QDir dir(QString::fromStdString(outputFolder)); for (size_t i = 0 ; i < dataCollection->Size(); ++i) { // Write Subcollection tag xmlFileStream << " <" << SUBCOLLECTION << " " << NAME << "=\"" << dataCollection->IndexToName(i) << "\" " << FILEPATH << "=\"" << dataCollection->GetDataFilePath(i) << "\" id=\"Col" << subColId << "\" >\n"; // Create Sub-Folder dir.mkpath(QString::fromStdString(dataCollection->IndexToName(i))); // Herein create data folders DataCollection* subCollections = dynamic_cast (dataCollection->GetData(i).GetPointer()); if (subCollections == nullptr) { MITK_ERROR<< "mitk::CollectionWriter::SaveCollectionToFolder: Container is illformed. Aborting"; return false; } for (size_t d = 0; d < subCollections->Size(); d++ ) { // Create Sub Paths QString subPath = QString::fromStdString(dataCollection->IndexToName(i))+"/"+QString::fromStdString(subCollections->IndexToName(d)); dir.mkpath(subPath); xmlFileStream << " <" << DATA << " " << NAME << "=\"" << subCollections->IndexToName(d) << "\" " << FILEPATH << "=\"" << subCollections->GetDataFilePath(d) << "\" id=\"Data" << dataId << "\" >\n"; DataCollection* itemCollections = dynamic_cast (subCollections->GetData(d).GetPointer()); if (itemCollections == nullptr) { MITK_ERROR<< "mitk::CollectionWriter::SaveCollectionToFolder: Container is illformed. Aborting"; return false; } for (size_t s = 0; s < itemCollections->Size(); s++) { if (filter.size() > 0) { bool isSelected = false; for (size_t f = 0; f < filter.size(); f++) { if (filter.at(f) == itemCollections->IndexToName(s) ) { isSelected = true; break; } } if (isSelected == false) continue; } - Image* image = dynamic_cast (itemCollections->GetData(s).GetPointer()); + QString fileName = dir.path() + dir.separator() + subPath + dir.separator() + QString::fromStdString(dataCollection->IndexToName(i)) + "_" + QString::fromStdString(subCollections->IndexToName(d)) + "_" + QString::fromStdString(itemCollections->IndexToName(s)); try { fileName += ".nrrd"; Image::Pointer image = itemCollections->GetMitkImage(s).GetPointer(); - IOUtil::SaveImage(image,fileName.toStdString()); + IOUtil::Save(image, fileName.toStdString()); } catch( const std::exception& e) { MITK_ERROR << "Caught exception: " << e.what(); } std::string relativeFilename = baseFolder.relativeFilePath(fileName).toStdString(); xmlFileStream << " <" << ITEM << " " << NAME << "=\"" <IndexToName(s) << "\" " << FILEPATH << "=\"" << "\" " << LINK << "=\"" << relativeFilename << "\" />\n"; } xmlFileStream << " \n"; dataId++; } xmlFileStream << " \n"; subColId++; } xmlFileStream << "\n"; xmlFileStream.flush(); xmlFileStream.close(); return true; } bool mitk::CollectionWriter::ExportCollectionToFolder(mitk::DataCollection *dataCollection, std::string xmlFile) { std::vector mods; return ExportCollectionToFolder(dataCollection,xmlFile, mods); } bool mitk::CollectionWriter::SaveCollection(mitk::DataCollection *dataCollection, std::vector filter, std::string xmlFile) { QDir origFilename = QFileInfo(dataCollection->GetXMLFile().c_str()).absoluteDir(); QString originalFolder = origFilename.path() + QDir::separator(); if (xmlFile == "") xmlFile = dataCollection->GetXMLFile(); QDir fileName = QFileInfo(xmlFile.c_str()).absoluteDir(); std::string outputFolder = fileName.path().toStdString() + QDir::separator().toLatin1(); QDir baseFolder(outputFolder.c_str()); std::ofstream xmlFileStream; xmlFileStream.open (xmlFile.c_str()); xmlFileStream << " \n"; xmlFileStream << "<" << COLLECTION << " " << NAME << "=\"" << dataCollection->GetName() << "\" >\n"; unsigned int subColId = 0; unsigned int dataId = 0; QDir dir(QString::fromStdString(outputFolder)); for (size_t i = 0 ; i < dataCollection->Size(); ++i) { // Write Subcollection tag xmlFileStream << " <" << SUBCOLLECTION << " " << NAME << "=\"" << dataCollection->IndexToName(i) << "\" " << FILEPATH << "=\"" << dataCollection->GetDataFilePath(i) << "\" id=\"Col" << subColId << "\" >\n"; // Create Sub-Folder dir.mkpath(QString::fromStdString(dataCollection->IndexToName(i))); // Herein create data folders DataCollection* subCollections = dynamic_cast (dataCollection->GetData(i).GetPointer()); if (subCollections == nullptr) { MITK_ERROR<< "mitk::CollectionWriter::SaveCollectionToFolder: Container is illformed. Aborting"; return false; } for (size_t d = 0; d < subCollections->Size(); d++ ) { // Create Sub Paths QString subPath = QString::fromStdString(dataCollection->IndexToName(i))+"/"+QString::fromStdString(subCollections->IndexToName(d)); dir.mkpath(subPath); xmlFileStream << " <" << DATA << " " << NAME << "=\"" << subCollections->IndexToName(d) << "\" " << FILEPATH << "=\"" << subCollections->GetDataFilePath(d) << "\" id=\"Data" << dataId << "\" >\n"; DataCollection* itemCollections = dynamic_cast (subCollections->GetData(d).GetPointer()); if (itemCollections == nullptr) { MITK_ERROR<< "mitk::CollectionWriter::SaveCollectionToFolder: Container is illformed. Aborting"; return false; } for (size_t s = 0; s < itemCollections->Size(); s++) { if (filter.size() > 0) { bool isSelected = false; for (size_t f = 0; f < filter.size(); f++) { if (filter.at(f) == itemCollections->IndexToName(s) ) { isSelected = true; break; } } if (isSelected == false) continue; } - Image* image = dynamic_cast (itemCollections->GetData(s).GetPointer()); + QString fileName; bool fullName = false; if (itemCollections->GetDataFilePath(s) != "") { fileName = originalFolder + QString::fromStdString(itemCollections->GetDataFilePath(s)); fullName = true; MITK_INFO << "original path: " << itemCollections->GetDataFilePath(s) ; } else fileName = dir.path() + dir.separator() + subPath + dir.separator() + QString::fromStdString(dataCollection->IndexToName(i)) + "_" + QString::fromStdString(subCollections->IndexToName(d)) + "_" + QString::fromStdString(itemCollections->IndexToName(s)); try { if (!fullName) fileName += ".nrrd"; Image::Pointer image = itemCollections->GetMitkImage(s).GetPointer(); - IOUtil::SaveImage(image,fileName.toStdString()); + IOUtil::Save(image,fileName.toStdString()); } catch( const std::exception& e) { MITK_ERROR << "Caught exception: " << e.what(); } std::string relativeFilename =baseFolder.relativeFilePath(fileName).toStdString(); xmlFileStream << " <" << ITEM << " " << NAME << "=\"" <IndexToName(s) << "\" " << FILEPATH << "=\"" << "\" " << LINK << "=\"" << relativeFilename << "\" />\n"; } xmlFileStream << " \n"; dataId++; } xmlFileStream << " \n"; subColId++; } xmlFileStream << "\n"; xmlFileStream.flush(); xmlFileStream.close(); return true; } bool mitk::CollectionWriter::FolderToXml(std::string folder, std::string collectionType, std::string xmlFile, std::vector filter, std::vector seriesNames) { // 1) Parse for folders QDir parseDir; parseDir.setFilter( QDir::Dirs | QDir::NoSymLinks | QDir::NoDotAndDotDot); parseDir.setPath(QString::fromStdString(folder)); QFileInfoList qFileList = parseDir.entryInfoList(); std::ofstream xmlFileStream; xmlFileStream.open (xmlFile.c_str()); xmlFileStream << " \n"; xmlFileStream << "<" << COLLECTION << " " << NAME << "=\"" << "GEN" << "\" >\n"; unsigned int dataId = 0; // now populate lists with files names, non-existing files will be marked with an empty string for (int i = 0; i < qFileList.size(); ++i) { // 2) For Each sub folder construct collectionType sub-folder std::string baseFolder = qFileList.at(i).absoluteFilePath().toStdString() + QDir::separator().toLatin1() + collectionType; MITK_INFO << "Processing : " << baseFolder; if (!QFileInfo(QString::fromStdString(baseFolder)).isDir()) { MITK_WARN << "Not a valid folder, skipping."; continue; } // 3) Parse each sub folder and extend XML file // Parse folder and look up all data, // after sanitation only fully available groups are included (that is all suffixes are found) CollectionReader::FileListType fileList = CollectionReader::SanitizeFileList(CollectionReader::GenerateFileLists(baseFolder, filter,true)); if (fileList.size() <= 0 || fileList.at(0).size() <= 0) continue; // Write Subcollection tag // try to extract date out of filename std::string name = GetName(fileList.at(0).at(0),filter.at(0)); xmlFileStream << " <" << SUBCOLLECTION << " " << NAME << "=\"" << name << "\" " << FILEPATH << "=\"\" id=\"Col" << i << "\" >\n"; for (unsigned int k=0; k < fileList.at(0).size(); ++k) // all groups have the same amount of items, so looking at 0 is ok. { std::string strDate = GetDate(fileList.at(0).at(k),filter.at(0)); xmlFileStream << " <" << DATA << " " << NAME << "=\"" << strDate << "\" " << " id=\"Data" << dataId << "\" >\n"; dataId++; for (unsigned int i=0; i< filter.size(); ++i) { std::string fileName = fileList.at(i).at(k); xmlFileStream << " <" << ITEM << " " << NAME << "=\"" << seriesNames.at(i) << "\" " << LINK << "=\"" << fileName << "\" />\n"; } xmlFileStream << " \n" ; } xmlFileStream << " \n"; } xmlFileStream << "\n"; xmlFileStream.flush(); xmlFileStream.close(); return true; } bool mitk::CollectionWriter::SingleFolderToXml(std::string folder, std::string xmlFile, std::vector filter, std::vector seriesNames, bool longDate, int skipUntil, float months) { std::ofstream xmlFileStream; xmlFileStream.open (xmlFile.c_str()); xmlFileStream << " \n"; xmlFileStream << "<" << COLLECTION << " " << NAME << "=\"" << "GEN" << "\" >\n"; unsigned int dataId = 0; // 1) // Parse folder and look up all data, // after sanitation only fully available groups are included (that is all suffixes are found) CollectionReader::FileListType fileList = CollectionReader::SanitizeFileList(CollectionReader::GenerateFileLists(folder, filter,true)); // Write Subcollection tag // try to extract date out of filename std::string name = GetName(fileList.at(0).at(0),filter.at(0),longDate); xmlFileStream << " <" << SUBCOLLECTION << " " << NAME << "=\"" << name << "\" " << FILEPATH << "=\"\" id=\"Col" << 0 << "\" >\n"; for (unsigned int k=skipUntil; k < fileList.at(0).size(); ++k) // all groups have the same amount of items, so looking at 0 is ok. { std::string strDate = GetDate(fileList.at(0).at(k),filter.at(0),true); xmlFileStream << " <" << DATA << " " << NAME << "=\"" << strDate << "\" " << " id=\"Data" << dataId << "\" >\n"; dataId++; for (unsigned int i=0; i< filter.size(); ++i) { std::string fileName = fileList.at(i).at(k); xmlFileStream << " <" << ITEM << " " << NAME << "=\"" << seriesNames.at(i) << "\" " << LINK << "=\"" << fileName << "\" />\n"; } // size_t ind = GetIndexForinXMonths(fileList,months,k,filter); // xmlFileStream << " <" << ITEM << " " << NAME << "=\"TARGET\" " << LINK << "=\"" << fileList.at(filter.size()-1).at(ind) << "\" />\n"; xmlFileStream << " \n" ; // check if target still exists for next step if (GetIndexForinXMonths(fileList,months,k+1,filter)== 0) break; } xmlFileStream << " \n"; xmlFileStream << "\n"; xmlFileStream.flush(); xmlFileStream.close(); return true; } size_t mitk::CollectionWriter::GetIndexForinXMonths(mitk::CollectionReader::FileListType fileList,float months, size_t curIndex,std::vector filter) { std::string strDate0 = GetDate(fileList.at(0).at(curIndex),filter.at(0),true); int year0 =std::atoi(strDate0.substr(0,4).c_str()); int month0 =std::atoi(strDate0.substr(5,2).c_str()); int day0 = std::atoi(strDate0.substr(8,2).c_str()); size_t bestIndex = 0; int bestFit = 1e5; for (size_t i=curIndex+1; i < fileList.at(0).size(); ++i) { std::string strDate = GetDate(fileList.at(0).at(i),filter.at(0),true); int year =std::atoi(strDate.substr(0,4).c_str()); int month =std::atoi(strDate.substr(5,2).c_str()); int day = std::atoi(strDate.substr(8,2).c_str()); int fit = std::fabs((months * 30 ) - (((year-year0)*360) +((month-month0)*30) + (day-day0))); // days difference from x months if (fit < bestFit) { bestFit = fit; bestIndex = i; } } return bestIndex; } diff --git a/Modules/Classification/DataCollection/Testing/mitkDataCollectionImageIteratorTest.cpp b/Modules/Classification/DataCollection/Testing/mitkDataCollectionImageIteratorTest.cpp index 9b8ac72430..045b9d4d56 100644 --- a/Modules/Classification/DataCollection/Testing/mitkDataCollectionImageIteratorTest.cpp +++ b/Modules/Classification/DataCollection/Testing/mitkDataCollectionImageIteratorTest.cpp @@ -1,166 +1,166 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include "mitkNumericTypes.h" class mitkDataCollectionTestClass { private: mitk::Image::Pointer im1; mitk::Image::Pointer im2; mitk::Image::Pointer im3; mitk::Image::Pointer im4; mitk::DataCollection::Pointer dataCol1; mitk::DataCollection::Pointer dataCol2; mitk::DataCollection::Pointer col1; public: mitk::DataCollection::Pointer m_Collection; void Init() { im1 = mitk::ImageGenerator::GenerateRandomImage(2,2,2,1,2,2,2,1,0); im2 = mitk::ImageGenerator::GenerateRandomImage(3,3,3,1,3,3,3,3,2); im3 = mitk::ImageGenerator::GenerateRandomImage(4,4,4,1,4,4,4,5,4); im4 = mitk::ImageGenerator::GenerateRandomImage(5,5,5,1,5,5,5,7,6); dataCol1 = mitk::DataCollection::New(); dataCol2 = mitk::DataCollection::New(); dataCol1->AddData(im1.GetPointer(),"T1"); dataCol1->AddData(im2.GetPointer(),"T2"); dataCol2->AddData(im3.GetPointer(),"T1"); dataCol2->AddData(im4.GetPointer(),"T2"); col1 = mitk::DataCollection::New(); col1->SetName("GnaBla"); col1->AddData(dataCol1.GetPointer(), "0001"); col1->AddData(dataCol2.GetPointer(), "0002"); m_Collection = mitk::DataCollection::New(); m_Collection->SetName("DummyCollection"); m_Collection->AddData(col1.GetPointer(), "C1"); } void IteratorReachesEnd() { Init(); mitk::DataCollectionSingleImageIterator iter(m_Collection, "T1"); int count = 0; while ( ! iter.IsAtEnd()) { ++iter; ++count; } MITK_TEST_CONDITION_REQUIRED(count == 2, "Number of Iterations match number of elements"); } void IteratorGivesRightFirstImage() { Init(); mitk::DataCollectionSingleImageIterator iter(m_Collection, "T1"); int spacing = iter.GetImage()->GetSpacing()[0]; MITK_INFO << "Spacing: " << spacing; MITK_TEST_CONDITION_REQUIRED(spacing == 2, "Image is the first image (Checked by Spacing)"); } void IteratorGivesRightSecondImage() { Init(); mitk::DataCollectionSingleImageIterator iter(m_Collection, "T1"); ++iter; int spacing = iter.GetImage()->GetSpacing()[0]; MITK_INFO << "Spacing: " << spacing; MITK_TEST_CONDITION_REQUIRED(spacing == 4, "Image is the second image (Checked by Spacing)"); } void AddingImageAtFirstPositionIsPossible() { Init(); mitk::DataCollectionSingleImageIterator iter(m_Collection, "T1"); mitk::Image::Pointer im = mitk::ImageGenerator::GenerateRandomImage(6,6,6,1,6,6,6,9,8); itk::Image::Pointer itkImage; mitk::CastToItkImage(im,itkImage); iter.AddImage(itkImage,"T3"); itk::DataObject::Pointer obj = (dynamic_cast (col1->GetData("0001").GetPointer()))->GetData("T3"); int spacing = dynamic_cast *> (obj.GetPointer())->GetSpacing()[0]; // MITK_INFO << "Spacing: " << spacing; MITK_TEST_CONDITION_REQUIRED(spacing == 6, "Image is the first image (Checked by Spacing)"); } void AddingImageAtSecondPositionIsPossible() { Init(); mitk::DataCollectionSingleImageIterator iter(m_Collection, "T1"); ++iter; mitk::Image::Pointer im = mitk::ImageGenerator::GenerateRandomImage(6,6,6,1,6,6,6,9,8); itk::Image::Pointer itkImage; mitk::CastToItkImage(im,itkImage); iter.AddImage(itkImage,"T3"); itk::DataObject::Pointer obj = (dynamic_cast (col1->GetData("0002").GetPointer()))->GetData("T3"); int spacing = dynamic_cast *> (obj.GetPointer())->GetSpacing()[0]; // MITK_INFO << "Spacing: " << spacing; MITK_TEST_CONDITION_REQUIRED(spacing == 6, "Image is the first image (Checked by Spacing)"); } }; -int mitkDataCollectionImageIteratorTest(int argc, char* argv[]) +int mitkDataCollectionImageIteratorTest(int, char**) { MITK_TEST_BEGIN("mitkDataCollectionImageIteratorTest"); //MITK_TEST_CONDITION_REQUIRED(true,"Here we test our condition"); mitkDataCollectionTestClass test; test.IteratorReachesEnd(); test.IteratorGivesRightFirstImage(); //test.IteratorGivesRightSecondImage(); // TODO: activate this test again as soon as bug 18365 is fixed. test.AddingImageAtFirstPositionIsPossible(); test.AddingImageAtSecondPositionIsPossible(); MITK_TEST_END(); } diff --git a/Modules/Classification/DataCollection/Utilities/mitkCollectionStatistic.cpp b/Modules/Classification/DataCollection/Utilities/mitkCollectionStatistic.cpp index ade1d7b4a9..e60d36159c 100644 --- a/Modules/Classification/DataCollection/Utilities/mitkCollectionStatistic.cpp +++ b/Modules/Classification/DataCollection/Utilities/mitkCollectionStatistic.cpp @@ -1,501 +1,500 @@ #include #include // DataCollection Stuff #include //stl stuff #include #include #include mitk::CollectionStatistic::CollectionStatistic() : m_GroundTruthValueToIndexMapper(nullptr) , m_TestValueToIndexMapper(nullptr) { } mitk::CollectionStatistic::~CollectionStatistic() { m_GroundTruthValueToIndexMapper = nullptr; m_TestValueToIndexMapper = nullptr; } void mitk::CollectionStatistic::SetCollection(mitk::DataCollection::Pointer collection) { m_Collection = collection; } mitk::DataCollection::Pointer mitk::CollectionStatistic::GetCollection() { return m_Collection; } void mitk::CollectionStatistic::SetClassCount (size_t count) { m_ClassCount = count; } size_t mitk::CollectionStatistic::GetClassCount() { return m_ClassCount; } void mitk::CollectionStatistic::SetGoldName(std::string name) { m_GroundTruthName = name; } std::string mitk::CollectionStatistic::GetGoldName() { return m_GroundTruthName; } void mitk::CollectionStatistic::SetTestName(std::string name) { m_TestName = name; } std::string mitk::CollectionStatistic::GetTestName() { return m_TestName; } void mitk::CollectionStatistic::SetGroundTruthValueToIndexMapper(const ValueToIndexMapper* mapper) { m_GroundTruthValueToIndexMapper = mapper; } const mitk::ValueToIndexMapper* mitk::CollectionStatistic::GetGroundTruthValueToIndexMapper(void) const { return m_GroundTruthValueToIndexMapper; } void mitk::CollectionStatistic::SetTestValueToIndexMapper(const ValueToIndexMapper* mapper) { m_TestValueToIndexMapper = mapper; } const mitk::ValueToIndexMapper* mitk::CollectionStatistic::GetTestValueToIndexMapper(void) const { return m_TestValueToIndexMapper; } int mitk::CollectionStatistic::IsInSameVirtualClass(unsigned char gold, unsigned char test) { int resultClass = -1; - for (int i = 0; i < m_ConnectionGold.size(); ++i) + for (std::size_t i = 0; i < m_ConnectionGold.size(); ++i) { if (m_ConnectionGold[i] == gold && m_ConnectionTest[i] == test) { resultClass = m_ConnectionClass[i]; break; } } return resultClass; } bool mitk::CollectionStatistic::Update() { if (m_GroundTruthValueToIndexMapper == nullptr) { MITK_ERROR << "m_GroundTruthValueToIndexMapper is nullptr"; return false; } if (m_TestValueToIndexMapper == nullptr) { MITK_ERROR << "m_TestValueToIndexMapper is nullptr"; return false; } - typedef itk::Image ImageType; DataCollectionImageIterator goldIter(m_Collection, m_GroundTruthName); DataCollectionImageIterator testIter(m_Collection, m_TestName); DataCollectionImageIterator maskIter(m_Collection, m_MaskName); int index = 0; while (!goldIter.IsAtEnd()) { std::size_t imageIndex = goldIter.GetImageIndex(); if (m_ImageClassStatistic.size() <= imageIndex) { MITK_INFO << "New Image: " << goldIter.GetFilePrefix(); m_ImageNames.push_back(goldIter.GetFilePrefix()); StatisticData statData; m_ImageStatistic.push_back(statData); DataVector data; - for (int i = 0; i < m_ClassCount; ++i) + for (std::size_t i = 0; i < m_ClassCount; ++i) { StatisticData stat; data.push_back(stat); } m_ImageClassStatistic.push_back(data); } if (maskIter.GetVoxel() <= 0) { ++goldIter; ++testIter; ++maskIter; continue; } ++index; unsigned char goldClass = m_GroundTruthValueToIndexMapper->operator()(goldIter.GetVoxel()); unsigned char testClass = m_TestValueToIndexMapper->operator()(testIter.GetVoxel()); if (goldClass == testClass) // True Positive { m_ImageStatistic[imageIndex].m_TruePositive += 1; - for (int i = 0; i < m_ClassCount; ++i) + for (std::size_t i = 0; i < m_ClassCount; ++i) { if (goldClass == i) // For the detected class it is a true positive { m_ImageClassStatistic[imageIndex][i].m_TruePositive += 1; } else // for all other classes than the detected it is a true negative { m_ImageClassStatistic[imageIndex][i].m_TrueNegative += 1; } } } else // No True Positive { m_ImageStatistic[imageIndex].m_FalseNegative += 1; m_ImageStatistic[imageIndex].m_FalsePositive += 1; - for (int i = 0; i < m_ClassCount; ++i) + for (std::size_t i = 0; i < m_ClassCount; ++i) { if (goldClass == i) // For the class in Goldstandard it is a false negative { m_ImageClassStatistic[imageIndex][i].m_FalseNegative += 1; } else if ( testClass == i) // For the test class it is a false positive { m_ImageClassStatistic[imageIndex][i].m_FalsePositive += 1; } else // For all other it is a true negative { m_ImageClassStatistic[imageIndex][i].m_TrueNegative += 1; } } } ++goldIter; ++testIter; ++maskIter; } MITK_INFO << "Evaluated " << index << " points"; return true; } void mitk::CollectionStatistic::Print(std::ostream& out, std::ostream& sout, bool withHeader, std::string label) { assert(m_ImageClassStatistic.size() > 0); assert(m_ImageClassStatistic[0].size() == m_ClassCount); if (withHeader) { sout << "Label;ImageName;"; - for (int i = 0; i < m_ClassCount; ++i) + for (std::size_t i = 0; i < m_ClassCount; ++i) { sout << "DICE-Class-"<< i << ";"; sout << "Jaccard-Class-"<< i << ";"; sout << "Sensitivity-Class-"<< i << ";"; sout << "Specificity-Class-"<< i << ";"; sout << "TP-Class-"<< i << ";"; sout << "TN-Class-"<< i << ";"; sout << "FP-Class-"<< i << ";"; sout << "FN-Class-"<< i << ";"; } sout << "DICE-MEAN"<< ";"; sout << "Jaccard-MEAN"<< ";"; sout << "Sensitivity-MEAN"<< ";"; sout << "Specificity-MEAN"<< ";"; sout << "TP-MEAN"<< ";"; sout << "TN-MEAN"<< ";"; sout << "FP-MEAN"<< ";"; sout << "FN-MEAN"<< ";"; sout << "DICE-WMEAN"<< ";"; sout << "Jaccard-WMEAN"<< ";"; sout << "Sensitivity-WMEAN"<< ";"; sout << "Specificity-WMEAN"<< ";"; sout << "TP-WMEAN"<< ";"; sout << "TN-WMEAN"<< ";"; sout << "FP-WMEAN"<< ";"; sout << "FN-WMEAN"<< ";"; sout << "COMPLETE-TRUE/FALSE"<< ";"; sout << "COMPLETE-TRUES"<< ";"; sout << "COMPLETE_FALSE"<< ";"; sout << std::endl; } out << std::setprecision(5); MITK_INFO << "m_ImageClassStatistic.size(): " << m_ImageClassStatistic.size(); - for (int i = 0; i < m_ImageClassStatistic.size(); ++i) + for (std::size_t i = 0; i < m_ImageClassStatistic.size(); ++i) { sout << label << ";"<< m_ImageNames[i]<<";"; StatisticData meanStat; StatisticData wMeanStat; double pointsSum = 0; out << "======================================================== Image " << std::setw(3) << i << " ========================================================" << std::endl; out << " Image ID : " << m_ImageNames[i] < mitk::CollectionStatistic::GetStatisticData(unsigned char c) const { std::vector statistics; for (size_t i = 0; i < m_ImageClassStatistic.size(); i++) { statistics.push_back(m_ImageClassStatistic[i][c]); } return statistics; } void mitk::CollectionStatistic::ComputeRMSD() { assert(m_ClassCount == 2); assert(m_GroundTruthValueToIndexMapper != nullptr); assert(m_TestValueToIndexMapper != nullptr); DataCollectionImageIterator groundTruthIter(m_Collection, m_GroundTruthName); DataCollectionImageIterator testIter(m_Collection, m_TestName); DataCollectionImageIterator maskIter(m_Collection, m_MaskName); typedef itk::Image LabelImage; typedef itk::Image ImageType; typedef itk::SignedDanielssonDistanceMapImageFilter DistanceMapFilterType; typedef itk::ConstantBoundaryCondition BoundaryConditionType; typedef itk::ConstShapedNeighborhoodIterator ConstNeighborhoodIteratorType; // Build up 6-neighborhood. Diagonal voxel are maybe not considered for distance map computation. // So 6-neighborhood avoids inconsistencies. ConstNeighborhoodIteratorType::OffsetType offset0 = {{ 0, 0, -1}}; ConstNeighborhoodIteratorType::OffsetType offset1 = {{ 0, 0, 1}}; ConstNeighborhoodIteratorType::OffsetType offset2 = {{ 0, -1, 0}}; ConstNeighborhoodIteratorType::OffsetType offset3 = {{ 0, 1, 0}}; ConstNeighborhoodIteratorType::OffsetType offset4 = {{-1, 0, 0}}; ConstNeighborhoodIteratorType::OffsetType offset5 = {{ 1, 0, 0}}; const int outsideVal = 17; itk::NeighborhoodIterator::RadiusType radius; radius.Fill(1); BoundaryConditionType bc; // if a neighborhood voxel is outside of the image region a default value is returned bc.SetConstant(outsideVal); ConstNeighborhoodIteratorType neighborhoodIter; ImageType::Pointer distanceImage; std::vector* currentImageStatistics = nullptr; unsigned int distanceBorderSamples = 0; double totalBorderRMSDistance = 0; int previousImageIndex = -1; while (!testIter.IsAtEnd()) { int currentImageIndex = testIter.GetImageIndex(); // prepare data for next image if (previousImageIndex != currentImageIndex) { previousImageIndex = currentImageIndex; currentImageStatistics = &(m_ImageClassStatistic.at(currentImageIndex)); distanceBorderSamples = 0; totalBorderRMSDistance = 0; // generate distance map from gold standard image DistanceMapFilterType::Pointer distanceMapFilter = DistanceMapFilterType::New(); distanceMapFilter->SetInput(groundTruthIter.GetImageIterator().GetImage()); distanceMapFilter->SetUseImageSpacing(true); distanceMapFilter->Update(); distanceImage = distanceMapFilter->GetOutput(); neighborhoodIter = ConstNeighborhoodIteratorType(radius, testIter.GetImageIterator().GetImage(), testIter.GetImageIterator().GetImage()->GetRequestedRegion()); neighborhoodIter.OverrideBoundaryCondition(&bc); // configure 6-neighborhood neighborhoodIter.ActivateOffset(offset0); neighborhoodIter.ActivateOffset(offset1); neighborhoodIter.ActivateOffset(offset2); neighborhoodIter.ActivateOffset(offset3); neighborhoodIter.ActivateOffset(offset4); neighborhoodIter.ActivateOffset(offset5); } unsigned char testClass = m_TestValueToIndexMapper->operator()(testIter.GetVoxel()); if ( maskIter.GetVoxel() > 0 && testClass != 0) { // check if it is a border voxel neighborhoodIter.SetLocation(testIter.GetImageIterator().GetIndex()); bool border = false; ConstNeighborhoodIteratorType::ConstIterator iter; for (iter = neighborhoodIter.Begin(); !iter.IsAtEnd(); iter++) { if (iter.Get() == outsideVal) { continue; } if (m_TestValueToIndexMapper->operator()(iter.Get()) != 1 ) { border = true; break; } } if (border) { double currentDistance = distanceImage->GetPixel(testIter.GetImageIterator().GetIndex()); totalBorderRMSDistance += currentDistance * currentDistance; ++distanceBorderSamples; // update immediately, so the final value is set after the iterator of the last image has reached the end double rmsd = std::sqrt(totalBorderRMSDistance / (double) distanceBorderSamples); currentImageStatistics->at(1).m_RMSD = rmsd; } } ++groundTruthIter; ++testIter; ++maskIter; } } diff --git a/Modules/Classification/DataCollection/Utilities/mitkCostingStatistic.cpp b/Modules/Classification/DataCollection/Utilities/mitkCostingStatistic.cpp index 23484f3ed5..1fc4db1a46 100644 --- a/Modules/Classification/DataCollection/Utilities/mitkCostingStatistic.cpp +++ b/Modules/Classification/DataCollection/Utilities/mitkCostingStatistic.cpp @@ -1,196 +1,196 @@ #include #include #include #include // DataCollection Stuff #include //stl stuff #include static void EnsureDataImageInCollection(mitk::DataCollection::Pointer collection, std::string origin, std::string target) { typedef itk::Image FeatureImage; typedef itk::Image LabelImage; if (collection->HasElement(origin)) { LabelImage::Pointer originImage = dynamic_cast(collection->GetData(origin).GetPointer()); MITK_INFO << "Creating new Element"; if (!collection->HasElement(target) && originImage.IsNotNull()) { MITK_INFO << "New image necessary"; FeatureImage::Pointer image = FeatureImage::New(); image->SetRegions(originImage->GetLargestPossibleRegion()); image->SetSpacing(originImage->GetSpacing()); image->SetOrigin(originImage->GetOrigin()); image->SetDirection(originImage->GetDirection()); image->Allocate(); collection->AddData(dynamic_cast(image.GetPointer()),target,""); } } for (std::size_t i = 0; i < collection->Size();++i) { mitk::DataCollection* newCol = dynamic_cast(collection->GetData(i).GetPointer()); if (newCol != 0) { EnsureDataImageInCollection(newCol, origin, target); } } } static void EnsureLabelImageInCollection(mitk::DataCollection::Pointer collection, std::string origin, std::string target) { typedef itk::Image FeatureImage; typedef itk::Image LabelImage; if (collection->HasElement(origin)) { LabelImage::Pointer originImage = dynamic_cast(collection->GetData(origin).GetPointer()); MITK_INFO << "Creating new Element"; if (!collection->HasElement(target) && originImage.IsNotNull()) { MITK_INFO << "New image necessary"; FeatureImage::Pointer image = FeatureImage::New(); image->SetRegions(originImage->GetLargestPossibleRegion()); image->SetSpacing(originImage->GetSpacing()); image->SetOrigin(originImage->GetOrigin()); image->SetDirection(originImage->GetDirection()); image->Allocate(); collection->AddData(dynamic_cast(image.GetPointer()),target,""); } } for (std::size_t i = 0; i < collection->Size();++i) { mitk::DataCollection* newCol = dynamic_cast(collection->GetData(i).GetPointer()); if (newCol != 0) { EnsureLabelImageInCollection(newCol, origin, target); } } } void mitk::CostingStatistic::SetCollection(mitk::DataCollection::Pointer collection) { m_Collection = collection; } mitk::DataCollection::Pointer mitk::CostingStatistic::GetCollection() { return m_Collection; } bool mitk::CostingStatistic::UpdateCollection() { EnsureDataImageInCollection(m_Collection, m_MaskName, m_CombinedProbabilityA); EnsureDataImageInCollection(m_Collection, m_MaskName, m_CombinedProbabilityB); EnsureLabelImageInCollection(m_Collection, m_MaskName, m_CombinedLabelName); std::vector > iterProbA; std::vector > iterProbB; - for (int i = 0; i < m_ProbabilityClassA.size(); ++i) + for (std::size_t i = 0; i < m_ProbabilityClassA.size(); ++i) { DataCollectionImageIterator iter(m_Collection, m_ProbabilityClassA[i]); iterProbA.push_back(iter); } - for (int i = 0; i < m_ProbabilityClassB.size(); ++i) + for (std::size_t i = 0; i < m_ProbabilityClassB.size(); ++i) { DataCollectionImageIterator iter(m_Collection, m_ProbabilityClassB[i]); iterProbB.push_back(iter); } DataCollectionImageIterator iterCombineA(m_Collection, m_CombinedProbabilityA); DataCollectionImageIterator iterCombineB(m_Collection, m_CombinedProbabilityB); DataCollectionImageIterator iterMask(m_Collection, m_MaskName); while (!iterMask.IsAtEnd()) { if (iterMask.GetVoxel() > 0) { double probA = 0; double probB = 0; - for (int i = 0; i < iterProbA.size(); ++i) + for (std::size_t i = 0; i < iterProbA.size(); ++i) { probA += iterProbA[i].GetVoxel(); } - for (int i = 0; i < iterProbB.size(); ++i) + for (std::size_t i = 0; i < iterProbB.size(); ++i) { probB += iterProbB[i].GetVoxel(); } iterCombineA.SetVoxel(probA * 100); iterCombineB.SetVoxel(probB * 100); } else { iterCombineA.SetVoxel(0.0); iterCombineB.SetVoxel(0.0); } ++iterCombineA; ++iterCombineB; ++iterMask; - for (int i = 0; i < iterProbA.size(); ++i) + for (std::size_t i = 0; i < iterProbA.size(); ++i) { ++(iterProbA[i]); } - for (int i = 0; i < iterProbB.size(); ++i) + for (std::size_t i = 0; i < iterProbB.size(); ++i) { ++(iterProbB[i]); } } return 0; } bool mitk::CostingStatistic::CalculateClass(double threshold) { DataCollectionImageIterator iterMask(m_Collection, m_MaskName); DataCollectionImageIterator iterLabel(m_Collection, m_CombinedLabelName); DataCollectionImageIterator iterCombineA(m_Collection, m_CombinedProbabilityA); DataCollectionImageIterator iterCombineB(m_Collection, m_CombinedProbabilityB); while (!iterMask.IsAtEnd()) { if (iterMask.GetVoxel() > 0) { double probA = iterCombineA.GetVoxel() / (iterCombineA.GetVoxel() + iterCombineB.GetVoxel()); probA *= 100; iterLabel.SetVoxel(probA >= threshold ? 1 : 2); } else { iterLabel.SetVoxel(0); } ++iterMask; ++iterLabel; ++iterCombineA; ++iterCombineB; } return true; } bool mitk::CostingStatistic::WriteStatistic(std::ostream &out,std::ostream &sout, double stepSize, std::string shortLabel) { UpdateCollection(); for (double threshold = 0 ; threshold <= 100; threshold += stepSize) { CalculateClass(threshold); std::stringstream ss; ss << shortLabel << ";" << threshold; mitk::CollectionStatistic stat; stat.SetCollection(m_Collection); stat.SetClassCount(2); stat.SetGoldName("GTV"); stat.SetTestName(m_CombinedLabelName); stat.SetMaskName(m_MaskName); stat.Update(); stat.Print(out, sout,false, ss.str()); } return true; -} \ No newline at end of file +} diff --git a/Modules/Core/include/mitkIPersistenceService.h b/Modules/Core/include/mitkIPersistenceService.h index 6cee4f7775..f800618ecb 100644 --- a/Modules/Core/include/mitkIPersistenceService.h +++ b/Modules/Core/include/mitkIPersistenceService.h @@ -1,374 +1,374 @@ /*=================================================================== 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 mitkIPersistenceService_h #define mitkIPersistenceService_h // mitk #include "mitkPropertyListReplacedObserver.h" #include "usGetModuleContext.h" #include "usModuleContext.h" #include "usServiceReference.h" // for microservices #include "mitkDataStorage.h" #include namespace mitk { /** * \ingroup MicroServices_Interfaces * * The central service for the persistence module * Basic idea is to create PropertyLists with a unique id using AddPropertyList(). A consumer * of this interface can write arbitrary information into this propertylist * Calling Save() and Load() will cause the Service to save and load the current set of propertlists from * a file in the user directory. * Using SetAutoLoadAndSave(true) will cause the service to load/save the property lists at application * start/stop. * Moreover, depending on the backend type, the service is connected to the SceneSerialization module, i.e. * the user will be asked whether to save/load the propertlists in/from the current ".mitk" file that is selected * by the user. */ class MITKCORE_EXPORT IPersistenceService { public: /** * If PropertyList with the given id exists, returns it. Otherwise creates a new one and returns it. * If id is empty a UUID will be created and set on the variable * If existed was passed, it is true if the PropertyList with that id existed, false otherwise * \return a valid PropertyList with a StringProperty "Id" containing the passed id */ virtual mitk::PropertyList::Pointer GetPropertyList(std::string &id, bool *existed = 0) = 0; /** * removes the PropertyList with the given id * \return true if PropertyList existed and could be removed, false otherwise */ virtual bool RemovePropertyList(std::string &id) = 0; /** * Get the default name of the PersistenceFile (the one that is loaded at startup) */ virtual std::string GetDefaultPersistenceFile() = 0; /** * \return The name of the Bool Property that specifies whether a DataNode is a Node carrying Persistence * PropertyLists */ virtual std::string GetPersistenceNodePropertyName() = 0; /** * Creates a vector of DataNodes that contain all PropertyLists. Additionally, the DataNodes * will have the property name set to the PropertyList's id and a BoolProperty equal to * GetPersistenceNodePropertyName() set to true. If ds is set the returned DataNodes will also be added to that DS. * \return vector of DataNodes with the described attributes */ virtual DataStorage::SetOfObjects::Pointer GetDataNodes(DataStorage *ds = 0) = 0; /** * Searches storage for persistent DataNodes, extracts and inserts the appended property lists to this service * \return true if at least one node was found from which a PropertyList could be restored */ virtual bool RestorePropertyListsFromPersistentDataNodes(const DataStorage *storage) = 0; /** * Save the current PropertyLists to fileName. If fileName is empty, a special file in the users home directory will * be * used. * if appendchanges is true, the file will not replaced but first loaded, then overwritten and then replaced * \return false if an error occured (cannot write to file), true otherwise */ virtual bool Save(const std::string &fileName = "", bool appendChanges = false) = 0; /** * Load PropertyLists from fileName. If fileName is empty, a special file in the users home directory will be used. * If enforeReload is false, the service will take care of modified time flags, i.e. it will not load a file * that was loaded before and did not change in the meantime or that was modified by the service itself * *ATTENTION*: If there are PropertyLists with the same id contained in the file, existing PropertyLists will be * overwritten! * \see AddPropertyListReplacedObserver() * \return false if an error occured (cannot load from file), true otherwise */ virtual bool Load(const std::string &fileName = "", bool enforeReload = true) = 0; /** * Using SetAutoLoadAndSave(true) will cause the service to load/save the property lists at application * start/stop. */ virtual void SetAutoLoadAndSave(bool autoLoadAndSave) = 0; /** * \return whether AutoLoading is activated or not */ virtual bool GetAutoLoadAndSave() = 0; /** * adds a observer which is informed if a propertyList gets replaced during a Load() procedure */ virtual void AddPropertyListReplacedObserver(PropertyListReplacedObserver *observer) = 0; /** * removes a specific observer */ virtual void RemovePropertyListReplacedObserver(PropertyListReplacedObserver *observer) = 0; /** * nothing to do here */ virtual ~IPersistenceService(); }; } // MACROS FOR AUTOMATIC SAVE FUNCTION #define PERSISTENCE_GET_MODULE_CONTEXT_FUNCTION us::GetModuleContext() #define PERSISTENCE_GET_SERVICE_MACRO \ mitk::IPersistenceService *persistenceService = 0; \ us::ModuleContext *context = PERSISTENCE_GET_MODULE_CONTEXT_FUNCTION; \ if (context) \ { \ us::ServiceReference persistenceServiceRef = \ context->GetServiceReference(); \ if (persistenceServiceRef) \ { \ persistenceService = dynamic_cast( \ context->GetService(persistenceServiceRef)); \ } \ } #define PERSISTENCE_GET_SERVICE_METHOD_MACRO \ mitk::IPersistenceService *GetPersistenceService() const \ { \ PERSISTENCE_GET_SERVICE_MACRO \ return persistenceService; \ } #define PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ \ public: \ bool Save(const std::string &fileName = "", bool appendChanges = false) \ { \ mitk::IPersistenceService *persistenceService = this->GetPersistenceService(); \ bool noError = persistenceService != 0; \ if (noError) \ this->ToPropertyList(); \ if (noError) \ noError = persistenceService->Save(fileName, appendChanges); \ return noError; \ } \ bool Load(const std::string &fileName = "", bool enforeReload = true) \ { \ mitk::IPersistenceService *persistenceService = this->GetPersistenceService(); \ bool noError = persistenceService != 0 && persistenceService->Load(fileName, enforeReload); \ if (noError) \ { \ this->FromPropertyList(); \ } \ return noError; \ } \ void ToPropertyList() \ { \ mitk::IPersistenceService *persistenceService = this->GetPersistenceService(); \ this->InitializePropertyListReplacedObserver(persistenceService); \ if (!persistenceService) \ return; \ mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(ID_MEMBER_NAME); #define PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ } \ void FromPropertyList() \ { \ mitk::IPersistenceService *persistenceService = this->GetPersistenceService(); \ this->InitializePropertyListReplacedObserver(persistenceService); \ if (!persistenceService) \ return; \ mitk::PropertyList::Pointer propList = persistenceService->GetPropertyList(ID_MEMBER_NAME); #define PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) \ } \ \ std::string GetId() const { return ID_MEMBER_NAME; } \ private: \ PERSISTENCE_GET_SERVICE_METHOD_MACRO \ struct MyPropertyListReplacedObserver : public mitk::PropertyListReplacedObserver \ { \ MyPropertyListReplacedObserver() : m_##THE_CLASS_NAME(0), m_PersistenceService(0) {} \ ~MyPropertyListReplacedObserver() \ { \ if (m_PersistenceService) \ m_PersistenceService->RemovePropertyListReplacedObserver(this); \ } \ - void AfterPropertyListReplaced(const std::string &id, mitk::PropertyList *propertyList) \ + void AfterPropertyListReplaced(const std::string &id, mitk::PropertyList *) \ { \ if (m_##THE_CLASS_NAME && m_##THE_CLASS_NAME->GetId() == id) \ m_##THE_CLASS_NAME->FromPropertyList(); \ } \ void Initialize(THE_CLASS_NAME *_##THE_CLASS_NAME, mitk::IPersistenceService *persistenceService) \ { \ m_##THE_CLASS_NAME = _##THE_CLASS_NAME; \ m_PersistenceService = persistenceService; \ if (m_PersistenceService) \ m_PersistenceService->AddPropertyListReplacedObserver(this); \ } \ \ private: \ THE_CLASS_NAME *m_##THE_CLASS_NAME; \ mitk::IPersistenceService *m_PersistenceService; \ }; \ MyPropertyListReplacedObserver m_MyPropertyListReplacedObserver; \ void InitializePropertyListReplacedObserver(mitk::IPersistenceService *persistenceService) \ { \ static bool observerInitialized = false; \ if (observerInitialized == false && persistenceService) \ { \ m_MyPropertyListReplacedObserver.Initialize(this, persistenceService); \ observerInitialized = true; \ } \ } #define PERSISTENCE_CREATE(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE2(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE3(THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE4( \ THE_CLASS_NAME, ID_MEMBER_NAME, PARAM_MEMBER_NAME, PARAM2_MEMBER_NAME, PARAM3_MEMBER_NAME, PARAM4_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE5(THE_CLASS_NAME, \ ID_MEMBER_NAME, \ PARAM_MEMBER_NAME, \ PARAM2_MEMBER_NAME, \ PARAM3_MEMBER_NAME, \ PARAM4_MEMBER_NAME, \ PARAM5_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Set(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Get(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE6(THE_CLASS_NAME, \ ID_MEMBER_NAME, \ PARAM_MEMBER_NAME, \ PARAM2_MEMBER_NAME, \ PARAM3_MEMBER_NAME, \ PARAM4_MEMBER_NAME, \ PARAM5_MEMBER_NAME, \ PARAM6_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Set(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Set(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Get(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Get(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE7(THE_CLASS_NAME, \ ID_MEMBER_NAME, \ PARAM_MEMBER_NAME, \ PARAM2_MEMBER_NAME, \ PARAM3_MEMBER_NAME, \ PARAM4_MEMBER_NAME, \ PARAM5_MEMBER_NAME, \ PARAM6_MEMBER_NAME, \ PARAM7_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Set(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Set(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ propList->Set(#PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Get(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Get(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ propList->Get(#PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) #define PERSISTENCE_CREATE8(THE_CLASS_NAME, \ ID_MEMBER_NAME, \ PARAM_MEMBER_NAME, \ PARAM2_MEMBER_NAME, \ PARAM3_MEMBER_NAME, \ PARAM4_MEMBER_NAME, \ PARAM5_MEMBER_NAME, \ PARAM6_MEMBER_NAME, \ PARAM7_MEMBER_NAME, \ PARAM8_MEMBER_NAME) \ PERSISTENCE_MACRO_START_PART(ID_MEMBER_NAME) \ propList->Set(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Set(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Set(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Set(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Set(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Set(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ propList->Set(#PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME); \ propList->Set(#PARAM8_MEMBER_NAME, PARAM8_MEMBER_NAME); \ PERSISTENCE_MACRO_MIDDLE_PART(ID_MEMBER_NAME) \ propList->Get(#PARAM_MEMBER_NAME, PARAM_MEMBER_NAME); \ propList->Get(#PARAM2_MEMBER_NAME, PARAM2_MEMBER_NAME); \ propList->Get(#PARAM3_MEMBER_NAME, PARAM3_MEMBER_NAME); \ propList->Get(#PARAM4_MEMBER_NAME, PARAM4_MEMBER_NAME); \ propList->Get(#PARAM5_MEMBER_NAME, PARAM5_MEMBER_NAME); \ propList->Get(#PARAM6_MEMBER_NAME, PARAM6_MEMBER_NAME); \ propList->Get(#PARAM7_MEMBER_NAME, PARAM7_MEMBER_NAME); \ propList->Get(#PARAM8_MEMBER_NAME, PARAM8_MEMBER_NAME); \ PERSISTENCE_MACRO_END_PART(THE_CLASS_NAME, ID_MEMBER_NAME) MITK_DECLARE_SERVICE_INTERFACE(mitk::IPersistenceService, "org.mitk.services.IPersistenceService") #endif diff --git a/Modules/Core/test/mitkDICOMLocaleTest.cpp b/Modules/Core/test/mitkDICOMLocaleTest.cpp index 264285663a..322e2b7fd1 100644 --- a/Modules/Core/test/mitkDICOMLocaleTest.cpp +++ b/Modules/Core/test/mitkDICOMLocaleTest.cpp @@ -1,137 +1,136 @@ /*=================================================================== 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. ===================================================================*/ /* This test is meant to reproduce the following error: - The machine or current user has a German locale. - This esp. means that stream IO expects the decimal separator as a comma: "," - DICOM files use a point "." as the decimal separator to be locale independent - The parser used by MITK (ITK's GDCM) seems to use the current locale instead of the "C" or "POSIX" locale - This leads to spacings (and probably other numbers) being trimmed/rounded, e.g. the correct spacing of 0.314 is read as 1.0 etc. */ #include "mitkDicomSeriesReader.h" #include "mitkIOUtil.h" #include "mitkImage.h" #include "mitkStandardFileLocations.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include #include class mitkDICOMLocaleTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkDICOMLocaleTestSuite); CPPUNIT_TEST_SUITE_ADD_CUSTOM_TESTS(addDICOMLocaleWithReferenceImageTests); CPPUNIT_TEST_SUITE_END(); private: // A custom method for adding a combination of filename and locale tests static void addDICOMLocaleWithReferenceImageTests(TestSuiteBuilderContextType &context) { std::vector fileArgs; fileArgs.push_back("spacing-ok-ct.dcm"); fileArgs.push_back("spacing-ok-mr.dcm"); fileArgs.push_back("spacing-ok-sc.dcm"); // load a reference DICOM file with German locales being set std::vector localeArgs; localeArgs.push_back("C"); localeArgs.push_back("de_DE"); localeArgs.push_back("de_DE.utf8"); localeArgs.push_back("de_DE.UTF8"); localeArgs.push_back("de_DE@euro"); localeArgs.push_back("German_Germany"); for (std::size_t fileIndex = 0; fileIndex < fileArgs.size(); ++fileIndex) { for (std::size_t localeIndex = 0; localeIndex < localeArgs.size(); ++localeIndex) { MITK_PARAMETERIZED_TEST_2(testLocaleWithReferenceImage, fileArgs[fileIndex], localeArgs[localeIndex]); } } } private: std::string m_FileName; std::string m_Locale; - bool m_SkipImageTest; - char *m_OldLocale; + bool m_SkipImageTest; void SetTestParameter() { std::vector parameter = GetTestParameter(); CPPUNIT_ASSERT(parameter.size() == 2); m_FileName = GetTestDataFilePath(parameter[0]); m_Locale = parameter[1]; } public: mitkDICOMLocaleTestSuite() : m_OldLocale(nullptr), m_SkipImageTest(false) {} // Change the current locale to m_Locale void setUp() override { m_SkipImageTest = false; m_OldLocale = nullptr; SetTestParameter(); try { m_OldLocale = setlocale(LC_ALL, nullptr); MITK_TEST_OUTPUT(<< " ** Changing locale from " << m_OldLocale << " to '" << m_Locale << "'") setlocale(LC_ALL, m_Locale.c_str()); std::cin.imbue(std::locale(m_Locale.c_str())); } catch (...) { MITK_TEST_OUTPUT(<< "Could not activate locale " << m_Locale) m_SkipImageTest = true; } } void tearDown() override { if (m_OldLocale) { setlocale(LC_ALL, m_OldLocale); std::cin.imbue(std::locale(m_OldLocale)); } } void testLocaleWithReferenceImage() { if (m_SkipImageTest) return; mitk::Image::Pointer image = mitk::IOUtil::LoadImage(m_FileName); CPPUNIT_ASSERT(image.IsNotNull()); // note importance of minor differences in spacings: // DICOM has order y-spacing, x-spacing, while in MITK we assume x-spacing, y-spacing (both meant for 0 and 1 index // in array) CPPUNIT_ASSERT_MESSAGE("incorrect x spacing", mitk::Equal(image->GetGeometry()->GetSpacing()[0], 0.3141592)); CPPUNIT_ASSERT_MESSAGE("incorrect y spacing ", mitk::Equal(image->GetGeometry()->GetSpacing()[1], 0.3411592)); } }; MITK_TEST_SUITE_REGISTRATION(mitkDICOMLocale) diff --git a/Modules/Core/test/mitkGrabItkImageMemoryTest.cpp b/Modules/Core/test/mitkGrabItkImageMemoryTest.cpp index a9f3c05372..d2646230d6 100644 --- a/Modules/Core/test/mitkGrabItkImageMemoryTest.cpp +++ b/Modules/Core/test/mitkGrabItkImageMemoryTest.cpp @@ -1,125 +1,124 @@ /*=================================================================== 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 "mitkITKImageImport.h" #include "mitkTestingMacros.h" #include "mitkImagePixelReadAccessor.h" #include #include "mitkImageAccessByItk.h" /** * An ITK-based filter for thresholding. * * The filter represents the typical usage of ITK-like filters inside MITK. It is to be called for an mitk::Image * by using the AccessByItk macro. The filter executes the binary threshold filter and imports the result into the * output by using the ImportItkImage method. * * @param output mitk::Image to hold the result of the filter * @param th[] two double values to set the lower/upper threshold */ //! [ItkThresholdFilter] template static void ItkThresholdFilter(const itk::Image *image, mitk::Image::Pointer &output, const double th[]) { typedef itk::Image InputImageType; - typedef itk::Image OutputImageType; typedef itk::ThresholdImageFilter ThresholdFilterType; typename ThresholdFilterType::Pointer thresholder = ThresholdFilterType::New(); thresholder->SetInput(image); thresholder->ThresholdOutside(th[0], th[1]); thresholder->Update(); try { output = mitk::GrabItkImageMemory(thresholder->GetOutput()); } catch (itk::ExceptionObject &) { MITK_TEST_FAILED_MSG(<< "Thresholding computation failed"); } } //! [ItkThresholdFilter] /** * Creates an mitk::Image, executes the binary threshold filter through AccessByItk and * checks whether the image data was correctly imported back to an mitk::Image. */ template bool Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue() { // data for 3x3x3 image const unsigned int dimensions[3] = {3, 3, 3}; auto image_data = new TPixel[27]; // ground truth for result check auto ground_truth = new TPixel[27]; double threshold[2] = {90.0, 180.0}; // fill image for (unsigned int i = 0; i < 27; i++) { image_data[i] = static_cast(i * 10); ground_truth[i] = 0; if (image_data[i] >= threshold[0] && image_data[i] <= threshold[1]) ground_truth[i] = static_cast(i * 10); } mitk::Image::Pointer input = mitk::Image::New(); input->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); input->SetImportVolume(image_data); //! [OutOfScopeCall] mitk::Image::Pointer output = mitk::Image::New(); AccessByItk_2(input, ItkThresholdFilter, output, threshold); //! [OutOfScopeCall] mitk::ImagePixelReadAccessor readAccessor(output); const TPixel *output_data = readAccessor.GetData(); bool equal = true; for (unsigned int i = 0; i < 27; i++) { equal &= (ground_truth[i] == output_data[i]); if (!equal) { MITK_INFO << " :: At position " << i << " : " << ground_truth[i] << " ? " << output_data[i] << "\n"; break; } } MITK_TEST_CONDITION(equal, " Imported output data equals the ground truth"); return equal; } int mitkGrabItkImageMemoryTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkGrabItkImageMemoryTest") Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import successful on 3D short"); Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImportWithinAccessByItkSucceded_ReturnsTrue(); // "Import succesfull on int"); MITK_TEST_END() } diff --git a/Modules/Core/test/mitkImportItkImageTest.cpp b/Modules/Core/test/mitkImportItkImageTest.cpp index 4987be7a1d..8517770ee6 100644 --- a/Modules/Core/test/mitkImportItkImageTest.cpp +++ b/Modules/Core/test/mitkImportItkImageTest.cpp @@ -1,311 +1,310 @@ /*=================================================================== 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 "mitkITKImageImport.h" #include "mitkImageCast.h" #include "mitkTestingMacros.h" #include "mitkImagePixelReadAccessor.h" #include #include /** * Create a test image with random pixel values. The image size is determined by the input parameter. * * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageRandom(short int size) { typedef typename itk::Image ImageType; - typedef typename ImageType::Pointer ImagePointer; itk::Size regionSize; regionSize.Fill(size); typename itk::RandomImageSource::Pointer randomImageSource = itk::RandomImageSource::New(); randomImageSource->SetNumberOfThreads(1); // to produce non-random results randomImageSource->SetSize(regionSize); randomImageSource->Update(); return randomImageSource->GetOutput(); } /** * Create a test vector image (with two components) with a single pixel value. The image size is determined by the input * parameter. * * @param value the pixel value the created image is filled with * @param size the number of voxels in each dimension */ template typename itk::VectorImage::Pointer CreateTestVectorImageFixedValue( size_t size, const itk::VariableLengthVector &value) { typedef typename itk::VectorImage ImageType; typedef typename ImageType::Pointer ImagePointer; typename ImageType::RegionType imageRegion; typename ImageType::RegionType::SizeType regionSize; regionSize.Fill(size); typename ImageType::RegionType::IndexType regionIndex; regionIndex.Fill(0); imageRegion.SetSize(regionSize); imageRegion.SetIndex(regionIndex); typename ImageType::SpacingType imageSpacing; imageSpacing.Fill(1.0f); typename ImageType::PointType imageOrigin; imageOrigin.Fill(0.0f); ImagePointer itkImage = ImageType::New(); itkImage->SetVectorLength(value.GetNumberOfElements()); itkImage->SetRegions(imageRegion); itkImage->SetOrigin(imageOrigin); itkImage->SetSpacing(imageSpacing); itkImage->Allocate(); itkImage->FillBuffer(value); return itkImage; } /** * Create a test image with a single pixel value. The image size is determined by the input parameter. * * @param value the pixel value the created image is filled with * @param size the number of voxels in each dimension */ template typename itk::Image::Pointer CreateTestImageFixedValue(size_t size, TPixel value) { typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; typename ImageType::RegionType imageRegion; typename ImageType::RegionType::SizeType regionSize; regionSize.Fill(size); typename ImageType::RegionType::IndexType regionIndex; regionIndex.Fill(0); imageRegion.SetSize(regionSize); imageRegion.SetIndex(regionIndex); typename ImageType::SpacingType imageSpacing; imageSpacing.Fill(1.0f); typename ImageType::PointType imageOrigin; imageOrigin.Fill(0.0f); ImagePointer itkImage = ImageType::New(); itkImage->SetRegions(imageRegion); itkImage->SetOrigin(imageOrigin); itkImage->SetSpacing(imageSpacing); itkImage->Allocate(); itkImage->FillBuffer(value); return itkImage; } /** * Compares the meta information of both given images for equality. */ template bool Assert_ImageMetaData_AreEqual(typename ImageType::Pointer itkImage, mitk::Image::Pointer mitkImage) { bool return_value = true; typename ImageType::RegionType itkRegion = itkImage->GetLargestPossibleRegion(); typename ImageType::SizeType itkImageSize = itkRegion.GetSize(); // check dimension for (unsigned int idx = 0; idx < mitkImage->GetDimension(); idx++) { return_value &= (itkImageSize[idx] == mitkImage->GetDimension(idx)); } MITK_TEST_CONDITION(return_value, " - Dimensions equal!") // check pixel type bool ptype_compare = (mitkImage->GetPixelType() == mitk::MakePixelType()); return_value &= ptype_compare; MITK_TEST_CONDITION(ptype_compare, " - Pixel types equal!") mitk::BaseGeometry *imageGeometry = mitkImage->GetGeometry(); const mitk::Point3D origin = imageGeometry->GetOrigin(); bool origin_compare = true; for (unsigned int idx = 0; idx < 3; idx++) { origin_compare &= (itkImage->GetOrigin()[idx] == origin[idx]); } return_value &= origin_compare; MITK_TEST_CONDITION(origin_compare, " - Origin equals!") return return_value; } /** * Generates a random itk image and imports it to mitk image through ImportItkImage and compares the values * voxel-wise afterwards */ template void Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: (Random Image, " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageRandom(5); mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage); itk::ImageRegionConstIteratorWithIndex iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); mitk::ImagePixelReadAccessor readAccessor(output_import); bool difference = false; while (!iter.IsAtEnd()) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex()); difference |= (ref != val); if (difference) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual(itkImage, output_import); MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison."); } /** * Generates an itk image with fixed pixel value and imports it to mitk image through ImportItkImage * and compares the values voxel-wise afterwards */ template void Assert_ItkImageImportSucceded_ReturnsTrue() { std::stringstream msg; msg << "Current type: " << VDimension << "D):" << typeid(TPixel).name() << "\n"; std::cout << msg.str(); bool assert_value = true; typedef typename itk::Image ImageType; typedef typename ImageType::Pointer ImagePointer; ImagePointer itkImage = CreateTestImageFixedValue(5, itk::NumericTraits::min()); mitk::Image::Pointer output_import = mitk::ImportItkImage(itkImage); itk::ImageRegionConstIteratorWithIndex iter(itkImage, itkImage->GetLargestPossibleRegion()); iter.GoToBegin(); mitk::ImagePixelReadAccessor readAccessor(output_import); bool difference = false; while (!iter.IsAtEnd()) { TPixel ref = iter.Get(); TPixel val = readAccessor.GetPixelByIndex(iter.GetIndex()); difference |= (ref != val); if (difference) { std::cout << iter.GetIndex() << ":" << ref << " ? " << val << "\n"; } ++iter; } assert_value = Assert_ImageMetaData_AreEqual(itkImage, output_import); MITK_TEST_CONDITION(assert_value && (!difference), "Pixel values are same in voxel-wise comparison."); } void Assert_ItkVectorImageImportAndCast_ReturnsTrue() { typedef itk::VectorImage ImageType; ImageType::PixelType value; value.SetSize(2); value.SetElement(0, 1); value.SetElement(0, 2); ImageType::Pointer itkImage = CreateTestVectorImageFixedValue(5, value); mitk::Image::Pointer mitkImage = mitk::ImportItkImage(itkImage); mitk::PixelType pixelType = mitkImage->GetPixelType(); MITK_TEST_CONDITION(pixelType.GetPixelType() == itk::ImageIOBase::VECTOR, "Vector image pixel type") MITK_TEST_CONDITION(pixelType.GetComponentType() == itk::ImageIOBase::SHORT, "Vector image component type") mitk::Image::Pointer mitkImage2; mitk::CastToMitkImage(itkImage, mitkImage2); mitk::PixelType pixelType2 = mitkImage2->GetPixelType(); MITK_TEST_CONDITION(pixelType == pixelType2, "ImportItkImage and CastToMitkImage produce same pixel types") ImageType::Pointer itkImageOut; mitk::CastToItkImage(mitkImage, itkImageOut); MITK_TEST_CONDITION(pixelType == mitk::MakePixelType(2), "MITK pixel type equals ITK pixel type") typedef itk::VectorImage IntImageType; IntImageType::Pointer itkIntImageOut; mitk::CastToItkImage(mitkImage, itkIntImageOut); MITK_TEST_CONDITION(!(pixelType == mitk::MakePixelType(2)), "MITK pixel type != ITK pixel type") mitk::Image::Pointer mitkImage3; mitk::CastToMitkImage(itkImageOut, mitkImage3); MITK_ASSERT_EQUAL(mitkImage, mitkImage3, "Equality for vector images"); } int mitkImportItkImageTest(int /*argc*/, char * /*argv*/ []) { MITK_TEST_BEGIN("mitkImportItkImageTest") Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImageImportSucceded_ReturnsTrue(); // "Import succesfull on int"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on int"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on 3D short"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on float"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on uchar"); Assert_ItkImageImportRandomValuesSucceded_ReturnsTrue(); // "Import succesfull on int"); Assert_ItkVectorImageImportAndCast_ReturnsTrue(); MITK_TEST_END() } diff --git a/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp b/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp index 4af47243e7..3c025ff466 100644 --- a/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp +++ b/Modules/Core/test/mitkLevelWindowManagerCppUnitTest.cpp @@ -1,155 +1,154 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkTestingMacros.h" #include #include "mitkLevelWindowManager.h" #include "mitkStandaloneDataStorage.h" #include #include #include #include #include class mitkLevelWindowManagerCppUnitTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLevelWindowManagerCppUnitTestSuite); MITK_TEST(TestMultiComponentRescaling); CPPUNIT_TEST_SUITE_END(); private: mitk::Image::Pointer m_mitkMultiComponentImage; mitk::Image::Pointer m_mitkImageComponent1; mitk::Image::Pointer m_mitkImageComponent2; public: void setUp() { typedef itk::Image ImageType; - typedef itk::VectorImage VectorImageType; typedef itk::ImageRegionIterator ImageIteratorType; typedef itk::ImageDuplicator DuplicatorType; typedef itk::ComposeImageFilter CompositeFilterType; // generate two images with one component ImageType::Pointer imageComponent1 = itk::Image::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size.Fill(5); ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); imageComponent1->SetRegions(region); imageComponent1->Allocate(); DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(imageComponent1); duplicator->Update(); ImageType::Pointer imageComponent2 = duplicator->GetOutput(); // give them differing data ImageIteratorType iterator1(imageComponent1, imageComponent1->GetLargestPossibleRegion()); iterator1.GoToBegin(); int i = 0; while (!iterator1.IsAtEnd()) { iterator1.Set((double)i); ++iterator1; ++i; } ImageIteratorType iterator2(imageComponent2, imageComponent2->GetLargestPossibleRegion()); iterator2.GoToBegin(); i = 2000; while (!iterator2.IsAtEnd()) { iterator2.Set((double)i); ++iterator2; ++i; } // copy into single VectorImage CompositeFilterType::Pointer compositeFilter = CompositeFilterType::New(); compositeFilter->SetInput(0, imageComponent1); compositeFilter->SetInput(1, imageComponent2); compositeFilter->Update(); itk::VectorImage::Pointer multiComponentImage = compositeFilter->GetOutput(); // cast images to mitk mitk::CastToMitkImage(multiComponentImage, m_mitkMultiComponentImage); mitk::CastToMitkImage(imageComponent1, m_mitkImageComponent1); mitk::CastToMitkImage(imageComponent2, m_mitkImageComponent2); } static mitk::LevelWindow getLevelWindowForImage(mitk::Image *image, unsigned component) { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); try { manager->SetDataStorage(ds); } catch (std::exception &e) { MITK_ERROR << "Exception: " << e.what(); } mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(image); ds->Add(node); node->SetBoolProperty("selected", true); node->SetIntProperty("Image.Displayed Component", component); mitk::LevelWindow levelWindow; node->GetLevelWindow(levelWindow); return levelWindow; // node is an image node because of predicates } void TestMultiComponentRescaling() { // compute level windows for the three images mitk::LevelWindow imageComponent1LevelWindow = getLevelWindowForImage(m_mitkImageComponent1, 0); mitk::LevelWindow imageComponent2LevelWindow = getLevelWindowForImage(m_mitkImageComponent2, 0); // calculate level window for second component in multi-component image mitk::LevelWindow multiComponentImageLevelWindow = getLevelWindowForImage(m_mitkMultiComponentImage, 1); // compare level window boundaries. the multicomponent image level window // should match the second image, since the second component was selected CPPUNIT_ASSERT_EQUAL_MESSAGE("default lower bounds equal", imageComponent2LevelWindow.GetDefaultLowerBound(), multiComponentImageLevelWindow.GetDefaultLowerBound()); CPPUNIT_ASSERT_EQUAL_MESSAGE("default upper bounds equal", imageComponent2LevelWindow.GetDefaultUpperBound(), multiComponentImageLevelWindow.GetDefaultUpperBound()); CPPUNIT_ASSERT_EQUAL_MESSAGE( "range equal", imageComponent2LevelWindow.GetRange(), multiComponentImageLevelWindow.GetRange()); CPPUNIT_ASSERT(imageComponent1LevelWindow.GetDefaultLowerBound() != multiComponentImageLevelWindow.GetDefaultLowerBound()); CPPUNIT_ASSERT(imageComponent1LevelWindow.GetDefaultUpperBound() != multiComponentImageLevelWindow.GetDefaultUpperBound()); } }; MITK_TEST_SUITE_REGISTRATION(mitkLevelWindowManagerCppUnit) diff --git a/Modules/Core/test/mitkLevelWindowManagerTest.cpp b/Modules/Core/test/mitkLevelWindowManagerTest.cpp index cc784c09c6..7a633438b1 100644 --- a/Modules/Core/test/mitkLevelWindowManagerTest.cpp +++ b/Modules/Core/test/mitkLevelWindowManagerTest.cpp @@ -1,633 +1,626 @@ /*=================================================================== 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 "itkMersenneTwisterRandomVariateGenerator.h" #include "mitkLevelWindowManager.h" #include "mitkRenderingModeProperty.h" #include "mitkStandaloneDataStorage.h" #include #include #include #include #include #include #include #include class mitkLevelWindowManagerTestClass { public: static void TestInstantiation() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); MITK_TEST_CONDITION_REQUIRED(manager.IsNotNull(), "Testing mitk::LevelWindowManager::New()"); } static void TestSetGetDataStorage() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); MITK_TEST_OUTPUT(<< "Creating DataStorage: "); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); bool success = true; try { manager->SetDataStorage(ds); } catch (std::exception &e) { success = false; MITK_ERROR << "Exception: " << e.what(); } MITK_TEST_CONDITION_REQUIRED(success, "Testing mitk::LevelWindowManager SetDataStorage() "); MITK_TEST_CONDITION_REQUIRED(ds == manager->GetDataStorage(), "Testing mitk::LevelWindowManager GetDataStorage "); } static void TestMethodsWithInvalidParameters() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); bool success = false; mitk::LevelWindowProperty::Pointer levelWindowProperty = mitk::LevelWindowProperty::New(); try { manager->SetLevelWindowProperty(levelWindowProperty); } catch (const mitk::Exception &) { success = true; } MITK_TEST_CONDITION(success, "Testing mitk::LevelWindowManager SetLevelWindowProperty with invalid parameter"); } static void TestOtherMethods() { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); MITK_TEST_CONDITION(manager->isAutoTopMost(), "Testing mitk::LevelWindowManager isAutoTopMost"); // It is not clear what the following code is supposed to test. The expression in // the catch(...) block does have no effect, so success is always true. // Related bugs are 13894 and 13889 /* bool success = true; try { mitk::LevelWindow levelWindow = manager->GetLevelWindow(); manager->SetLevelWindow(levelWindow); } catch (...) { success == false; } MITK_TEST_CONDITION(success,"Testing mitk::LevelWindowManager GetLevelWindow() and SetLevelWindow()"); */ manager->SetAutoTopMostImage(true); MITK_TEST_CONDITION(manager->isAutoTopMost(), "Testing mitk::LevelWindowManager isAutoTopMost()"); } static void TestRemoveObserver(std::string testImageFile) { mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); // add multiple objects to the data storage => multiple observers should be created mitk::Image::Pointer image1 = mitk::IOUtil::LoadImage(testImageFile); mitk::DataNode::Pointer node1 = mitk::DataNode::New(); node1->SetData(image1); mitk::Image::Pointer image2 = mitk::IOUtil::LoadImage(testImageFile); mitk::DataNode::Pointer node2 = mitk::DataNode::New(); node2->SetData(image2); ds->Add(node1); ds->Add(node2); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 2, "Test if nodes have been added"); MITK_TEST_CONDITION_REQUIRED( static_cast(manager->GetRelevantNodes()->size()) == manager->GetNumberOfObservers(), "Test if number of nodes is similar to number of observers"); mitk::Image::Pointer image3 = mitk::IOUtil::LoadImage(testImageFile); mitk::DataNode::Pointer node3 = mitk::DataNode::New(); node3->SetData(image3); ds->Add(node3); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 3, "Test if another node have been added"); MITK_TEST_CONDITION_REQUIRED( static_cast(manager->GetRelevantNodes()->size()) == manager->GetNumberOfObservers(), "Test if number of nodes is similar to number of observers"); ds->Remove(node1); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 2, "Deleted node 1 (test GetRelevantNodes())"); MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 2, "Deleted node 1 (test GetNumberOfObservers())"); ds->Remove(node2); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 1, "Deleted node 2 (test GetRelevantNodes())"); MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 1, "Deleted node 2 (test GetNumberOfObservers())"); ds->Remove(node3); MITK_TEST_CONDITION_REQUIRED(manager->GetRelevantNodes()->size() == 0, "Deleted node 3 (test GetRelevantNodes())"); MITK_TEST_CONDITION_REQUIRED(manager->GetNumberOfObservers() == 0, "Deleted node 3 (test GetNumberOfObservers())"); } static bool VerifyRenderingModes() { bool ok = (mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR == 1) && (mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_LEVELWINDOW_COLOR == 2) && (mitk::RenderingModeProperty::LOOKUPTABLE_COLOR == 3) && (mitk::RenderingModeProperty::COLORTRANSFERFUNCTION_COLOR == 4); return ok; } static void TestLevelWindowSliderVisibility(std::string testImageFile) { bool renderingModesValid = mitkLevelWindowManagerTestClass::VerifyRenderingModes(); if (!renderingModesValid) { MITK_ERROR << "Exception: Image Rendering.Mode property value types inconsistent."; } mitk::LevelWindowManager::Pointer manager; manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); // add multiple objects to the data storage => multiple observers should be created mitk::Image::Pointer image1 = mitk::IOUtil::LoadImage(testImageFile); mitk::DataNode::Pointer node1 = mitk::DataNode::New(); node1->SetData(image1); ds->Add(node1); // mitk::DataNode::Pointer node1 = mitk::IOUtil::LoadDataNode( testImageFile ); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); std::vector nodeVec; // nodeVec.resize( 3 ); nodeVec.push_back(node1); nodeVec.push_back(node2); nodeVec.push_back(node3); typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; RandomGeneratorType::Pointer rnd = RandomGeneratorType::New(); rnd->Initialize(); for (unsigned int i = 0; i < 8; ++i) { unsigned int parity = i; for (unsigned int img = 0; img < 3; ++img) { if (parity & 1) { int mode = rnd->GetIntegerVariate() % 3; nodeVec[img]->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New(mode)); } else { int mode = rnd->GetIntegerVariate() % 2; nodeVec[img]->SetProperty("Image Rendering.Mode", mitk::RenderingModeProperty::New(3 + mode)); } parity >>= 1; } MITK_TEST_CONDITION( renderingModesValid && ((!manager->GetLevelWindowProperty() && !i) || (manager->GetLevelWindowProperty() && i)), "Testing level window property member according to rendering mode"); } } static void TestSetLevelWindowProperty(std::string testImageFile) { mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); // add multiple objects to the data storage => multiple observers should be created mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); node3->SetIntProperty("layer", 1); node2->SetIntProperty("layer", 2); node1->SetIntProperty("layer", 3); manager->SetAutoTopMostImage(true); bool isImageForLevelWindow1, isImageForLevelWindow2, isImageForLevelWindow3; node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 1."); manager->SetAutoTopMostImage(false); mitk::LevelWindowProperty::Pointer prop = dynamic_cast(node2->GetProperty("levelwindow")); manager->SetLevelWindowProperty(prop); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 2."); prop = dynamic_cast(node3->GetProperty("levelwindow")); manager->SetLevelWindowProperty(prop); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); prop = dynamic_cast(node1->GetProperty("levelwindow")); manager->SetLevelWindowProperty(prop); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); } static void TestImageForLevelWindowOnVisibilityChange(std::string testImageFile) { mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); // add multiple objects to the data storage => multiple observers should be created mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); node3->SetIntProperty("layer", 1); node2->SetIntProperty("layer", 2); node1->SetIntProperty("layer", 3); manager->SetAutoTopMostImage(false); bool isImageForLevelWindow1, isImageForLevelWindow2, isImageForLevelWindow3; node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing initial imageForLevelWindow setting."); node1->SetVisibility(false); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 2."); node2->SetVisibility(false); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); node3->SetVisibility(false); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(!isImageForLevelWindow1 && !isImageForLevelWindow2 && isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); node1->SetVisibility(true); node1->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow1); node2->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow2); node3->GetBoolProperty("imageForLevelWindow", isImageForLevelWindow3); MITK_TEST_CONDITION(isImageForLevelWindow1 && !isImageForLevelWindow2 && !isImageForLevelWindow3, "Testing exclusive imageForLevelWindow property for node 3."); } static void TestImageForLevelWindowOnRandomPropertyChange(std::string testImageFile) { typedef std::vector BoolVecType; - typedef BoolVecType::iterator IteratorType; typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; // initialize the data storage mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); node3->SetIntProperty("layer", 1); node2->SetIntProperty("layer", 2); node1->SetIntProperty("layer", 3); // node visibilities std::vector nodesVisible; nodesVisible.resize(3); std::fill(nodesVisible.begin(), nodesVisible.end(), true); // which node has the level window std::vector nodesForLevelWindow; nodesForLevelWindow.resize(3); std::fill(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), false); // the nodes themselves std::vector nodes; nodes.push_back(node1); nodes.push_back(node2); nodes.push_back(node3); // status quo manager->SetAutoTopMostImage(false); bool lvlWin1, lvlWin2, lvlWin3; node1->GetBoolProperty("imageForLevelWindow", lvlWin1); node2->GetBoolProperty("imageForLevelWindow", lvlWin2); node3->GetBoolProperty("imageForLevelWindow", lvlWin3); MITK_TEST_CONDITION(lvlWin1 && !lvlWin2 && !lvlWin3, "Testing initial imageForLevelWindow setting."); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; // prepare randomized visibility changes RandomGeneratorType::Pointer ranGen = RandomGeneratorType::New(); ranGen->Initialize(); int ranCount = 100; int validCount = 0; int invalidCount = 0; int mustHaveLvlWindow = 4; for (int run = 0; run < ranCount; ++run) { // toggle node visibility int ran = ranGen->GetIntegerVariate(2); nodes[ran]->SetBoolProperty("imageForLevelWindow", !nodesForLevelWindow[ran]); // one node must have the level window std::vector::const_iterator found = std::find(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), true); if (found == nodesForLevelWindow.end()) { break; } // all invisible? found = std::find(nodesVisible.begin(), nodesVisible.end(), true); if (!nodesForLevelWindow[ran]) { mustHaveLvlWindow = pow(2, 2 - ran); } else { mustHaveLvlWindow = 4; } // get the current status node1->GetBoolProperty("imageForLevelWindow", lvlWin1); node2->GetBoolProperty("imageForLevelWindow", lvlWin2); node3->GetBoolProperty("imageForLevelWindow", lvlWin3); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; int hasLevelWindow = 0; for (int i = 0; i < 3; ++i) { if (nodesForLevelWindow[i]) { hasLevelWindow += pow(2, 2 - i); } } validCount += hasLevelWindow == mustHaveLvlWindow ? 1 : 0; // test sensitivity int falseran = 0; while (falseran == 0) { falseran = ranGen->GetIntegerVariate(7); } BoolVecType falseNodes; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin1 : lvlWin1); falseran >>= 1; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin2 : lvlWin2); falseran >>= 1; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin3 : lvlWin3); int falseLevelWindow = 0; for (int i = 0; i < 3; ++i) { if (falseNodes[i]) { falseLevelWindow += pow(2, 2 - i); } } invalidCount += falseLevelWindow == mustHaveLvlWindow ? 0 : 1; // in case of errors proceed anyway mustHaveLvlWindow = hasLevelWindow; } MITK_TEST_CONDITION(validCount == ranCount, "Testing proper node for level window property."); MITK_TEST_CONDITION(invalidCount == ranCount, "Sensitivity test."); } static void TestImageForLevelWindowOnRandomVisibilityChange(std::string testImageFile) { typedef std::vector BoolVecType; - typedef BoolVecType::iterator IteratorType; typedef itk::Statistics::MersenneTwisterRandomVariateGenerator RandomGeneratorType; // initialize the data storage mitk::LevelWindowManager::Pointer manager = mitk::LevelWindowManager::New(); mitk::StandaloneDataStorage::Pointer ds = mitk::StandaloneDataStorage::New(); manager->SetDataStorage(ds); mitk::DataNode::Pointer node3 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node2 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); mitk::DataNode::Pointer node1 = mitk::IOUtil::Load(testImageFile, *ds)->GetElement(0); node3->SetIntProperty("layer", 1); node2->SetIntProperty("layer", 2); node1->SetIntProperty("layer", 3); // node visibilities std::vector nodesVisible; nodesVisible.resize(3); std::fill(nodesVisible.begin(), nodesVisible.end(), true); // which node has the level window std::vector nodesForLevelWindow; nodesForLevelWindow.resize(3); std::fill(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), false); // the nodes themselves std::vector nodes; nodes.push_back(node1); nodes.push_back(node2); nodes.push_back(node3); // status quo manager->SetAutoTopMostImage(false); bool lvlWin1, lvlWin2, lvlWin3; node1->GetBoolProperty("imageForLevelWindow", lvlWin1); node2->GetBoolProperty("imageForLevelWindow", lvlWin2); node3->GetBoolProperty("imageForLevelWindow", lvlWin3); MITK_TEST_CONDITION(lvlWin1 && !lvlWin2 && !lvlWin3, "Testing initial imageForLevelWindow setting."); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; // prepare randomized visibility changes RandomGeneratorType::Pointer ranGen = RandomGeneratorType::New(); ranGen->Initialize(); int ranCount = 100; int validCount = 0; int invalidCount = 0; int mustHaveLvlWindow = 4; for (int run = 0; run < ranCount; ++run) { // toggle node visibility int ran = ranGen->GetIntegerVariate(2); nodesVisible[ran] = !nodesVisible[ran]; nodes[ran]->SetVisibility(nodesVisible[ran]); // one node must have the level window std::vector::const_iterator found = std::find(nodesForLevelWindow.begin(), nodesForLevelWindow.end(), true); if (found == nodesForLevelWindow.end()) { break; } int ind = found - nodesForLevelWindow.begin(); // all invisible? found = std::find(nodesVisible.begin(), nodesVisible.end(), true); bool allInvisible = (found == nodesVisible.end()); // which node shall get the level window now if (!allInvisible && !nodesVisible[ind]) { int count = 0; for (std::vector::const_iterator it = nodesVisible.begin(); it != nodesVisible.end(); ++it, ++count) { if (*it) { mustHaveLvlWindow = pow(2, 2 - count); break; } } } // get the current status node1->GetBoolProperty("imageForLevelWindow", lvlWin1); node2->GetBoolProperty("imageForLevelWindow", lvlWin2); node3->GetBoolProperty("imageForLevelWindow", lvlWin3); nodesForLevelWindow[0] = lvlWin1; nodesForLevelWindow[1] = lvlWin2; nodesForLevelWindow[2] = lvlWin3; int hasLevelWindow = 0; for (int i = 0; i < 3; ++i) { if (nodesForLevelWindow[i]) { hasLevelWindow += pow(2, 2 - i); } } - if (hasLevelWindow != mustHaveLvlWindow) - { - int n = 5; - } - validCount += hasLevelWindow == mustHaveLvlWindow ? 1 : 0; // test sensitivity int falseran = 0; while (falseran == 0) { falseran = ranGen->GetIntegerVariate(7); } BoolVecType falseNodes; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin1 : lvlWin1); falseran >>= 1; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin2 : lvlWin2); falseran >>= 1; falseNodes.push_back((falseran & 1) == 1 ? !lvlWin3 : lvlWin3); int falseLevelWindow = 0; for (int i = 0; i < 3; ++i) { if (falseNodes[i]) { falseLevelWindow += pow(2, 2 - i); } } invalidCount += falseLevelWindow == mustHaveLvlWindow ? 0 : 1; // in case of errors proceed anyway mustHaveLvlWindow = hasLevelWindow; } MITK_TEST_CONDITION(validCount == ranCount, "Testing proper node for level window property."); MITK_TEST_CONDITION(invalidCount == ranCount, "Sensitivity test."); } }; int mitkLevelWindowManagerTest(int argc, char *args[]) { MITK_TEST_BEGIN("mitkLevelWindowManager"); MITK_TEST_CONDITION_REQUIRED(argc >= 2, "Testing if test file is given."); std::string testImage = args[1]; mitkLevelWindowManagerTestClass::TestInstantiation(); mitkLevelWindowManagerTestClass::TestSetGetDataStorage(); mitkLevelWindowManagerTestClass::TestMethodsWithInvalidParameters(); mitkLevelWindowManagerTestClass::TestOtherMethods(); mitkLevelWindowManagerTestClass::TestRemoveObserver(testImage); mitkLevelWindowManagerTestClass::TestLevelWindowSliderVisibility(testImage); mitkLevelWindowManagerTestClass::TestSetLevelWindowProperty(testImage); mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnVisibilityChange(testImage); mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnRandomVisibilityChange(testImage); mitkLevelWindowManagerTestClass::TestImageForLevelWindowOnRandomPropertyChange(testImage); MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkPointSetTest.cpp b/Modules/Core/test/mitkPointSetTest.cpp index 0e8312d0e0..4b2d6f00e6 100644 --- a/Modules/Core/test/mitkPointSetTest.cpp +++ b/Modules/Core/test/mitkPointSetTest.cpp @@ -1,488 +1,487 @@ /*=================================================================== 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 "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include #include #include #include /** * TestSuite for PointSet stuff not only operating on an empty PointSet */ class mitkPointSetTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPointSetTestSuite); MITK_TEST(TestIsNotEmpty); MITK_TEST(TestSetSelectInfo); MITK_TEST(TestGetNumberOfSelected); MITK_TEST(TestSearchSelectedPoint); MITK_TEST(TestGetPointIfExists); MITK_TEST(TestSwapPointPositionUpwards); MITK_TEST(TestSwapPointPositionUpwardsNotPossible); MITK_TEST(TestSwapPointPositionDownwards); MITK_TEST(TestSwapPointPositionDownwardsNotPossible); MITK_TEST(TestCreateHoleInThePointIDs); MITK_TEST(TestInsertPointWithPointSpecification); MITK_TEST(TestRemovePointInterface); MITK_TEST(TestMaxIdAccess); MITK_TEST(TestInsertPointAtEnd); CPPUNIT_TEST_SUITE_END(); private: mitk::PointSet::Pointer pointSet; static const mitk::PointSet::PointIdentifier selectedPointId = 2; public: void setUp() override { // Create PointSet pointSet = mitk::PointSet::New(); // add some points mitk::Point3D point2, point3, point4; point2.Fill(3); point3.Fill(4); point4.Fill(5); pointSet->InsertPoint(2, point2); pointSet->InsertPoint(3, point3); pointSet->InsertPoint(4, point4); mitk::Point3D point1; mitk::FillVector3D(point1, 1.0, 2.0, 3.0); pointSet->InsertPoint(1, point1); mitk::Point3D point0; point0.Fill(1); pointSet->InsertPoint(0, point0); // select point with id 2 pointSet->SetSelectInfo(2, true); } void tearDown() override { pointSet = nullptr; } void TestIsNotEmpty() { // PointSet can not be empty! CPPUNIT_ASSERT_EQUAL_MESSAGE("check if the PointSet is not empty ", true, !pointSet->IsEmptyTimeStep(0)); /* std::cout << "check if the PointSet is not empty "; if (pointSet->IsEmpty(0)) { std::cout<<"[FAILED]"<SetSelectInfo(4, true); CPPUNIT_ASSERT_EQUAL_MESSAGE("check SetSelectInfo", true, pointSet->GetSelectInfo(4)); /* if (!pointSet->GetSelectInfo(2)) { std::cout<<"[FAILED]"<SearchSelectedPoint() == (int)selectedPointId); /* if( pointSet->SearchSelectedPoint() != 4) { std::cout<<"[FAILED]"<GetNumberOfSelected() == 1); /* if(pointSet->GetNumberOfSelected() != 1) { std::cout<<"[FAILED]"<GetPointIfExists(4, &tmpPoint); CPPUNIT_ASSERT_EQUAL_MESSAGE("check GetPointIfExists: ", true, tmpPoint == point4); /* if (tmpPoint != point5) { std::cout<<"[FAILED]"<GetPoint(1); pointSet->SwapPointPosition(1, true); tempPoint = pointSet->GetPoint(0); CPPUNIT_ASSERT_EQUAL_MESSAGE("check SwapPointPosition upwards", true, point == tempPoint); /* if(point != tempPoint) { std::cout<<"[FAILED]"<SwapPointPosition(0, true)); /* if(pointSet->SwapPointPosition(0, true)) { std::cout<<"[FAILED]"<GetPoint(0); pointSet->SwapPointPosition(0, false); tempPoint = pointSet->GetPoint(1); CPPUNIT_ASSERT_EQUAL_MESSAGE("check SwapPointPosition down", true, point == tempPoint); /* if(point != tempPoint) { std::cout<<"[FAILED]"<SetPoint(id, point); // Check SwapPointPosition downwards not possible CPPUNIT_ASSERT_EQUAL_MESSAGE( "check SwapPointPosition downwards not possible", false, pointSet2->SwapPointPosition(id, false)); /* if(pointSet->SwapPointPosition(1, false)) { std::cout<<"[FAILED]"<InsertPoint(10, p10); pointSet->InsertPoint(11, p11); pointSet->InsertPoint(12, p12); CPPUNIT_ASSERT_EQUAL_MESSAGE("add points with id 10, 11, 12: ", true, (pointSet->IndexExists(10) == true) || (pointSet->IndexExists(11) == true) || (pointSet->IndexExists(12) == true)); // check OpREMOVE ExecuteOperation int id = 11; auto doOp = new mitk::PointOperation(mitk::OpREMOVE, point, id); pointSet->ExecuteOperation(doOp); CPPUNIT_ASSERT_EQUAL_MESSAGE("remove point id 11: ", false, pointSet->IndexExists(id)); /* if(pointSet->IndexExists(id)) { std::cout<<"[FAILED]"<ExecuteOperation(doOp); delete doOp; // check OpMOVEPOINTUP ExecuteOperation doOp = new mitk::PointOperation(mitk::OpMOVEPOINTUP, p12, 12); pointSet->ExecuteOperation(doOp); delete doOp; mitk::PointSet::PointType newP10 = pointSet->GetPoint(10); mitk::PointSet::PointType newP12 = pointSet->GetPoint(12); CPPUNIT_ASSERT_EQUAL_MESSAGE( "check PointOperation OpMOVEPOINTUP for point id 12:", true, ((newP10 == p12) && (newP12 == p10))); // check OpMOVEPOINTDOWN ExecuteOperation doOp = new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, p10, 10); pointSet->ExecuteOperation(doOp); delete doOp; newP10 = pointSet->GetPoint(10); newP12 = pointSet->GetPoint(12); CPPUNIT_ASSERT_EQUAL_MESSAGE( "check PointOperation OpMOVEPOINTDOWN for point id 10: ", true, ((newP10 == p10) && (newP12 == p12))); } void TestInsertPointWithPointSpecification() { // check InsertPoint with PointSpecification mitk::Point3D point5; mitk::Point3D tempPoint; point5.Fill(7); pointSet->SetPoint(5, point5, mitk::PTEDGE); tempPoint = pointSet->GetPoint(5); CPPUNIT_ASSERT_EQUAL_MESSAGE("check InsertPoint with PointSpecification", true, tempPoint == point5); /* if (tempPoint != point5) { std::cout<<"[FAILED]"<Clone(); mitk::PointSet::Pointer refPsLastRemoved = mitk::PointSet::New(); mitk::Point3D point0, point1, point2, point3, point4; point0.Fill(1); refPsLastRemoved->InsertPoint(0, point0); mitk::FillVector3D(point1, 1.0, 2.0, 3.0); refPsLastRemoved->InsertPoint(1, point1); point2.Fill(3); point3.Fill(4); refPsLastRemoved->InsertPoint(2, point2); refPsLastRemoved->InsertPoint(3, point3); mitk::PointSet::Pointer refPsMiddleRemoved = mitk::PointSet::New(); refPsMiddleRemoved->InsertPoint(0, point0); refPsMiddleRemoved->InsertPoint(1, point1); refPsMiddleRemoved->InsertPoint(3, point3); // remove non-existent point bool removed = pointSet->RemovePointIfExists(5, 0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove non-existent point", false, removed); MITK_ASSERT_EQUAL(pointSet, psClone, "No changes made"); // remove point from non-existent time-step removed = pointSet->RemovePointIfExists(1, 1); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove non-existent point", false, removed); MITK_ASSERT_EQUAL(pointSet, psClone, "No changes made"); // remove max id from non-existent time-step mitk::PointSet::PointsIterator maxIt = pointSet->RemovePointAtEnd(2); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove max id point from non-existent time step", true, maxIt == pointSet->End(2)); MITK_ASSERT_EQUAL(pointSet, psClone, "No changes made"); // remove max id from empty point set mitk::PointSet::Pointer emptyPS = mitk::PointSet::New(); maxIt = emptyPS->RemovePointAtEnd(0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove max id point from non-existent time step", true, maxIt == emptyPS->End(0)); int size = emptyPS->GetSize(0); unsigned int pointSetSeriesSize = emptyPS->GetPointSetSeriesSize(); CPPUNIT_ASSERT_EQUAL_MESSAGE("Nothing added", true, size == 0 && pointSetSeriesSize == 1); // remove max id point maxIt = pointSet->RemovePointAtEnd(0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Point id 4 removed", false, pointSet->IndexExists(4)); MITK_ASSERT_EQUAL(pointSet, refPsLastRemoved, "No changes made"); mitk::PointSet::PointIdentifier id = maxIt.Index(); mitk::PointSet::PointType refPt; refPt[0] = 4.0; refPt[1] = 4.0; refPt[2] = 4.0; mitk::PointSet::PointType pt = maxIt.Value(); bool equal = mitk::Equal(refPt, pt); CPPUNIT_ASSERT_EQUAL_MESSAGE("Returned iterator pointing at max id", true, id == 3 && equal); // remove middle point removed = pointSet->RemovePointIfExists(2, 0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove point id 2", true, removed); MITK_ASSERT_EQUAL(pointSet, refPsMiddleRemoved, "Point removed"); } void TestMaxIdAccess() { typedef mitk::PointSet::PointIdentifier IdType; typedef mitk::PointSet::PointsIterator PointsIteratorType; PointsIteratorType empty; mitk::Point3D new1, new2, new3, new4, refMaxPt; new1.Fill(4); new2.Fill(5); new3.Fill(6); new4.Fill(7); refMaxPt.Fill(5); pointSet->SetPoint(0, new1, 2); pointSet->InsertPoint(1, new2, 2); pointSet->InsertPoint(3, new3, 2); pointSet->InsertPoint(6, new4, 2); PointsIteratorType maxIt = pointSet->GetMaxId(1); empty = pointSet->End(1); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check empty time step max id.", true, maxIt == empty); maxIt = pointSet->GetMaxId(3); empty = pointSet->End(3); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check non-existent time step max id.", true, maxIt == empty); maxIt = pointSet->GetMaxId(0); empty = pointSet->End(0); IdType maxId = maxIt.Index(); mitk::Point3D maxPt = maxIt.Value(); bool equal = mitk::Equal(maxPt, refMaxPt); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 0 max id iterator.", false, maxIt == empty); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 0 max id.", true, maxId == 4); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 0 max id point.", true, equal); maxIt = pointSet->GetMaxId(2); empty = pointSet->End(2); maxId = maxIt.Index(); maxPt = maxIt.Value(); equal = mitk::Equal(maxPt, new4); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 2 max id iterator.", false, maxIt == empty); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 2 max id.", true, maxId == 6); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 2 max id point.", true, equal); } void TestInsertPointAtEnd() { typedef mitk::PointSet::PointType PointType; - typedef mitk::PointSet::PointIdentifier IndexType; PointType new1, new2, new3, new4, refMaxPt; new1.Fill(4); new2.Fill(5); new3.Fill(6); new4.Fill(7); pointSet->SetPoint(1, new1, 2); pointSet->InsertPoint(3, new2, 2); pointSet->InsertPoint(4, new3, 2); pointSet->InsertPoint(6, new4, 2); PointType in1, in2, in3, in4; in1.Fill(8); in2.Fill(9); in3.Fill(10); in4.Fill(11); mitk::PointSet::Pointer refPs1 = pointSet->Clone(); refPs1->SetPoint(5, in1, 0); mitk::PointSet::Pointer refPs2 = pointSet->Clone(); refPs2->SetPoint(5, in1, 0); refPs2->SetPoint(0, in2, 1); mitk::PointSet::Pointer refPs3 = pointSet->Clone(); refPs3->SetPoint(5, in1, 0); refPs3->SetPoint(0, in2, 1); refPs3->SetPoint(7, in3, 2); mitk::PointSet::Pointer refPs4 = pointSet->Clone(); refPs4->SetPoint(5, in1, 0); refPs4->SetPoint(0, in2, 1); refPs4->SetPoint(7, in3, 2); refPs4->SetPoint(0, in4, 7); pointSet->InsertPoint(in1, 0); MITK_ASSERT_EQUAL(pointSet, refPs1, "Check point insertion for time step 0."); pointSet->InsertPoint(in2, 1); MITK_ASSERT_EQUAL(pointSet, refPs2, "Check point insertion for time step 1."); pointSet->InsertPoint(in3, 2); MITK_ASSERT_EQUAL(pointSet, refPs3, "Check point insertion for time step 2."); pointSet->InsertPoint(in4, 7); MITK_ASSERT_EQUAL(pointSet, refPs4, "Check point insertion for time step 7."); } }; MITK_TEST_SUITE_REGISTRATION(mitkPointSet) diff --git a/Modules/Core/test/mitkRotatedSlice4DTest.cpp b/Modules/Core/test/mitkRotatedSlice4DTest.cpp index 4df7dac2ac..ab305b682b 100644 --- a/Modules/Core/test/mitkRotatedSlice4DTest.cpp +++ b/Modules/Core/test/mitkRotatedSlice4DTest.cpp @@ -1,87 +1,87 @@ /*=================================================================== 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 "mitkExtractSliceFilter.h" #include "mitkIOUtil.h" #include "mitkImagePixelReadAccessor.h" #include "mitkImageTimeSelector.h" #include "mitkInteractionConst.h" #include "mitkRotationOperation.h" #include "mitkTestingMacros.h" #include "time.h" /* * The mitkRotatedSlice4DTest loads a 4D image and extracts a specifically rotated slice in each time step's volume. */ -int mitkRotatedSlice4DTest(int argc, char *argv[]) +int mitkRotatedSlice4DTest(int, char *argv[]) { MITK_TEST_BEGIN("mitkRotatedSlice4DTest"); std::string filename = argv[1]; // load 4D image mitk::Image::Pointer image4D = mitk::IOUtil::LoadImage(filename); // check inputs if (image4D.IsNull()) { MITK_INFO << "Could not load the file"; return false; } // for each time step... for (unsigned int ts = 0; ts < image4D->GetTimeSteps(); ts++) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(image4D); timeSelector->SetTimeNr(ts); timeSelector->Update(); mitk::Image::Pointer image3D = timeSelector->GetOutput(); int sliceNumber = 5; mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(image3D->GetGeometry(), mitk::PlaneGeometry::Frontal, sliceNumber, true, false); // rotate about an arbitrary point and axis... float angle = 30; mitk::Point3D point; point.Fill(sliceNumber); mitk::Vector3D rotationAxis; rotationAxis[0] = 1; rotationAxis[1] = 2; rotationAxis[2] = 3; rotationAxis.Normalize(); // Create Rotation Operation mitk::RotationOperation *op = new mitk::RotationOperation(mitk::OpROTATE, point, rotationAxis, angle); plane->ExecuteOperation(op); delete op; // Now extract mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(); extractor->SetInput(image3D); extractor->SetWorldGeometry(plane); extractor->Update(); mitk::Image::Pointer extractedPlane; extractedPlane = extractor->GetOutput(); std::stringstream ss; ss << " : Valid slice in timestep " << ts; MITK_TEST_CONDITION_REQUIRED(extractedPlane.IsNotNull(), ss.str().c_str()); } MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkSlicedGeometry3DTest.cpp b/Modules/Core/test/mitkSlicedGeometry3DTest.cpp index 65db326c85..a5378b6c78 100644 --- a/Modules/Core/test/mitkSlicedGeometry3DTest.cpp +++ b/Modules/Core/test/mitkSlicedGeometry3DTest.cpp @@ -1,184 +1,176 @@ /*=================================================================== 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 #include #include #include 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(numberOfSlices)); slicedGeometry->SetOrigin(origin); slicedGeometry->SetSpacing(spacing); 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 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(x, y, z); } static mitk::Vector3D createVector(mitk::ScalarType x, mitk::ScalarType y, mitk::ScalarType z) { return createArray(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/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp b/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp index 771a6ab4e9..8390b79d90 100644 --- a/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp +++ b/Modules/DICOMReader/src/mitkITKDICOMSeriesReaderHelper.cpp @@ -1,460 +1,458 @@ /*=================================================================== 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 MBILOG_ENABLE_DEBUG #include #define BOOST_DATE_TIME_NO_LIB //Prevent unnecessary/unwanted auto link in this compilation when activating boost libraries in the MITK superbuild //It is necessary because BOOST_ALL_DYN_LINK overwrites BOOST_DATE_TIME_NO_LIB #if defined(BOOST_ALL_DYN_LINK) #undef BOOST_ALL_DYN_LINK #endif #include #include "mitkITKDICOMSeriesReaderHelper.h" #include "mitkITKDICOMSeriesReaderHelper.txx" #include "mitkDICOMGDCMTagScanner.h" #include "mitkArbitraryTimeGeometry.h" #include "dcvrda.h" const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::AcquisitionDateTag = mitk::DICOMTag( 0x0008, 0x0022 ); const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::AcquisitionTimeTag = mitk::DICOMTag( 0x0008, 0x0032 ); const mitk::DICOMTag mitk::ITKDICOMSeriesReaderHelper::TriggerTimeTag = mitk::DICOMTag( 0x0018, 0x1060 ); #define switch3DCase( IOType, T ) \ case IOType: \ return LoadDICOMByITK( filenames, correctTilt, tiltInfo, io ); bool mitk::ITKDICOMSeriesReaderHelper::CanHandleFile( const std::string& filename ) { MITK_DEBUG << "ITKDICOMSeriesReaderHelper::CanHandleFile " << filename; itk::GDCMImageIO::Pointer tester = itk::GDCMImageIO::New(); return tester->CanReadFile( filename.c_str() ); } mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load( const StringContainer& filenames, bool correctTilt, const GantryTiltInformation& tiltInfo ) { if ( filenames.empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; return nullptr; // this is not actually an error but the result is very simple } typedef itk::GDCMImageIO DcmIoType; DcmIoType::Pointer io = DcmIoType::New(); try { if ( io->CanReadFile( filenames.front().c_str() ) ) { io->SetFileName( filenames.front().c_str() ); io->ReadImageInformation(); if ( io->GetPixelType() == itk::ImageIOBase::SCALAR ) { switch ( io->GetComponentType() ) { switch3DCase(DcmIoType::UCHAR, unsigned char) switch3DCase(DcmIoType::CHAR, char) switch3DCase( DcmIoType::USHORT, unsigned short) switch3DCase(DcmIoType::SHORT, short) switch3DCase(DcmIoType::UINT, unsigned int) switch3DCase(DcmIoType::INT, int) switch3DCase( DcmIoType::ULONG, long unsigned int) switch3DCase(DcmIoType::LONG, long int) switch3DCase(DcmIoType::FLOAT, float) switch3DCase(DcmIoType::DOUBLE, double) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } else if ( io->GetPixelType() == itk::ImageIOBase::RGB ) { switch ( io->GetComponentType() ) { switch3DCase(DcmIoType::UCHAR, itk::RGBPixel) switch3DCase( DcmIoType::CHAR, itk::RGBPixel) switch3DCase(DcmIoType::USHORT, itk::RGBPixel) switch3DCase(DcmIoType::SHORT, itk::RGBPixel) switch3DCase( DcmIoType::UINT, itk::RGBPixel) switch3DCase(DcmIoType::INT, itk::RGBPixel) switch3DCase(DcmIoType::ULONG, itk::RGBPixel) switch3DCase(DcmIoType::LONG, itk::RGBPixel) switch3DCase( DcmIoType::FLOAT, itk::RGBPixel) switch3DCase(DcmIoType::DOUBLE, itk::RGBPixel) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } MITK_ERROR << "Unsupported DICOM pixel type"; return nullptr; } } catch ( const itk::MemoryAllocationError& e ) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch ( const std::exception& e ) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch ( ... ) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return nullptr; } #define switch3DnTCase( IOType, T ) \ case IOType: \ return LoadDICOMByITK3DnT( filenamesLists, correctTilt, tiltInfo, io ); mitk::Image::Pointer mitk::ITKDICOMSeriesReaderHelper::Load3DnT( const StringContainerList& filenamesLists, bool correctTilt, const GantryTiltInformation& tiltInfo ) { if ( filenamesLists.empty() || filenamesLists.front().empty() ) { MITK_DEBUG << "Calling LoadDicomSeries with empty filename string container. Probably invalid application logic."; return nullptr; // this is not actually an error but the result is very simple } typedef itk::GDCMImageIO DcmIoType; DcmIoType::Pointer io = DcmIoType::New(); try { if ( io->CanReadFile( filenamesLists.front().front().c_str() ) ) { io->SetFileName( filenamesLists.front().front().c_str() ); io->ReadImageInformation(); if ( io->GetPixelType() == itk::ImageIOBase::SCALAR ) { switch ( io->GetComponentType() ) { switch3DnTCase(DcmIoType::UCHAR, unsigned char) switch3DnTCase(DcmIoType::CHAR, char) switch3DnTCase(DcmIoType::USHORT, unsigned short) switch3DnTCase( DcmIoType::SHORT, short) switch3DnTCase(DcmIoType::UINT, unsigned int) switch3DnTCase(DcmIoType::INT, int) switch3DnTCase(DcmIoType::ULONG, long unsigned int) switch3DnTCase(DcmIoType::LONG, long int) switch3DnTCase(DcmIoType::FLOAT, float) switch3DnTCase(DcmIoType::DOUBLE, double) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } else if ( io->GetPixelType() == itk::ImageIOBase::RGB ) { switch ( io->GetComponentType() ) { switch3DnTCase(DcmIoType::UCHAR, itk::RGBPixel) switch3DnTCase(DcmIoType::CHAR, itk::RGBPixel) switch3DnTCase( DcmIoType::USHORT, itk::RGBPixel) switch3DnTCase(DcmIoType::SHORT, itk::RGBPixel) switch3DnTCase(DcmIoType::UINT, itk::RGBPixel) switch3DnTCase( DcmIoType::INT, itk::RGBPixel) switch3DnTCase(DcmIoType::ULONG, itk::RGBPixel) switch3DnTCase(DcmIoType::LONG, itk::RGBPixel) switch3DnTCase( DcmIoType::FLOAT, itk::RGBPixel) switch3DnTCase(DcmIoType::DOUBLE, itk::RGBPixel) default : MITK_ERROR << "Found unsupported DICOM scalar pixel type: (enum value) " << io->GetComponentType(); } } MITK_ERROR << "Unsupported DICOM pixel type"; return nullptr; } } catch ( const itk::MemoryAllocationError& e ) { MITK_ERROR << "Out of memory. Cannot load DICOM series: " << e.what(); } catch ( const std::exception& e ) { MITK_ERROR << "Error encountered when loading DICOM series:" << e.what(); } catch ( ... ) { MITK_ERROR << "Unspecified error encountered when loading DICOM series."; } return nullptr; } bool ConvertDICOMDateTimeString( const std::string& dateString, const std::string& timeString, OFDateTime& time ) { OFString content( timeString.c_str() ); if ( !dateString.empty() ) { content = OFString( dateString.c_str() ).append( content ); } else { // This is a workaround for DICOM data that has an AquisitionTime but no AquisitionDate. // In this case, we use the current date. That's not really nice, but is absolutely OK // as we're only interested in the time anyways... OFString currentDate; DcmDate::getCurrentDate( currentDate ); content = currentDate.append( content ); } const OFCondition result = DcmDateTime::getOFDateTimeFromString( content, time ); return result.good(); } boost::posix_time::ptime ConvertOFDateTimeToPTime( const OFDateTime& time ) { const boost::gregorian::date boostDate( time.getDate().getYear(), time.getDate().getMonth(), time.getDate().getDay() ); const boost::posix_time::time_duration boostTime = boost::posix_time::hours( time.getTime().getHour() ) + boost::posix_time::minutes( time.getTime().getMinute() ) + boost::posix_time::seconds( time.getTime().getSecond() ) + boost::posix_time::milliseconds( time.getTime().getMilliSecond() ); boost::posix_time::ptime result( boostDate, boostTime ); return result; } OFDateTime GetLowerDateTime( const OFDateTime& time1, const OFDateTime& time2 ) { OFDateTime result = time1; if ( ( time2.getDate() < time1.getDate() ) || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() < time1.getTime() ) ) ) { result = time2; } return result; } OFDateTime GetUpperDateTime( const OFDateTime& time1, const OFDateTime& time2 ) { OFDateTime result = time1; if ( ( time2.getDate() > time1.getDate() ) || ( ( time2.getDate() == time1.getDate() ) && ( time2.getTime() > time1.getTime() ) ) ) { result = time2; } return result; } double ComputeMiliSecDuration( const OFDateTime& start, const OFDateTime& stop ) { const boost::posix_time::ptime startTime = ConvertOFDateTimeToPTime( start ); const boost::posix_time::ptime stopTime = ConvertOFDateTimeToPTime( stop ); ::boost::posix_time::time_duration duration = stopTime - startTime; return duration.total_milliseconds(); } bool mitk::ITKDICOMSeriesReaderHelper::ExtractDateTimeBoundsAndTriggerOfTimeStep( const StringContainer& filenamesOfTimeStep, DateTimeBounds& bounds, TimeBounds& triggerBounds) { DICOMGDCMTagScanner::Pointer filescanner = DICOMGDCMTagScanner::New(); filescanner->SetInputFiles(filenamesOfTimeStep); filescanner->AddTag(AcquisitionDateTag); filescanner->AddTag(AcquisitionTimeTag); filescanner->AddTag(TriggerTimeTag); filescanner->Scan(); const DICOMDatasetAccessingImageFrameList frameList = filescanner->GetFrameInfoList(); bool result = false; bool firstAq = true; bool firstTr = true; triggerBounds = TimeBounds(0.0); for (DICOMDatasetAccessingImageFrameList::const_iterator pos = frameList.cbegin(); pos != frameList.cend(); ++pos) { const std::string aqDateStr = (*pos)->GetTagValueAsString(AcquisitionDateTag).value; const std::string aqTimeStr = (*pos)->GetTagValueAsString(AcquisitionTimeTag).value; const std::string triggerTimeStr = (*pos)->GetTagValueAsString(TriggerTimeTag).value; OFDateTime aqDateTime; const bool convertAqResult = ConvertDICOMDateTimeString(aqDateStr, aqTimeStr, aqDateTime); OFBool convertTriggerResult; mitk::ScalarType triggerTime = OFStandard::atof(triggerTimeStr.c_str(), &convertTriggerResult); if (convertAqResult) { if (firstAq) { bounds[0] = aqDateTime; bounds[1] = aqDateTime; firstAq = false; } else { bounds[0] = GetLowerDateTime(bounds[0], aqDateTime); bounds[1] = GetUpperDateTime(bounds[1], aqDateTime); } result = true; } if (convertTriggerResult) { if (firstTr) { triggerBounds[0] = triggerTime; triggerBounds[1] = triggerTime; firstTr = false; } else { triggerBounds[0] = std::min(triggerBounds[0], triggerTime); triggerBounds[1] = std::max(triggerBounds[1], triggerTime); } result = true; } } return result; }; bool mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeStep( const StringContainer& filenamesOfTimeStep, TimeBounds& bounds, const OFDateTime& baselineDateTime ) { DateTimeBounds aqDTBounds; TimeBounds triggerBounds; bool result = ExtractDateTimeBoundsAndTriggerOfTimeStep(filenamesOfTimeStep, aqDTBounds, triggerBounds); mitk::ScalarType lowerBound = ComputeMiliSecDuration( baselineDateTime, aqDTBounds[0] ); mitk::ScalarType upperBound = ComputeMiliSecDuration( baselineDateTime, aqDTBounds[1] ); if ( lowerBound < mitk::eps || upperBound < mitk::eps ) { lowerBound = triggerBounds[0]; upperBound = triggerBounds[1]; } bounds[0] = lowerBound; bounds[1] = upperBound; return result; }; mitk::ITKDICOMSeriesReaderHelper::TimeBoundsList mitk::ITKDICOMSeriesReaderHelper::ExtractTimeBoundsOfTimeSteps( const StringContainerList& filenamesOfTimeSteps ) { TimeBoundsList result; OFDateTime baseLine; - bool baseLineSet = false; // extract the timebounds DateTimeBounds baselineDateTimeBounds; TimeBounds triggerBounds; StringContainerList::const_iterator pos = filenamesOfTimeSteps.cbegin(); ExtractDateTimeBoundsAndTriggerOfTimeStep(*pos, baselineDateTimeBounds, triggerBounds); baseLine = baselineDateTimeBounds[0]; - baseLineSet = true; // timebounds for baseline is 0 TimeBounds bounds( 0.0 ); result.push_back( bounds ); // iterate over the remaining timesteps for ( ++pos; pos != filenamesOfTimeSteps.cend(); ++pos ) { TimeBounds bounds( 0.0 ); TimeBounds dateTimeBounds; // extract the timebounds relative to the baseline if ( ExtractTimeBoundsOfTimeStep( *pos, dateTimeBounds, baseLine ) ) { bounds[0] = dateTimeBounds[0]; bounds[1] = dateTimeBounds[1]; } result.push_back( bounds ); } return result; }; mitk::TimeGeometry::Pointer mitk::ITKDICOMSeriesReaderHelper::GenerateTimeGeometry( const BaseGeometry* templateGeometry, const TimeBoundsList& boundsList ) { TimeGeometry::Pointer timeGeometry; double check = 0.0; const auto boundListSize = boundsList.size(); for ( std::size_t pos = 0; pos < boundListSize; ++pos ) { check += boundsList[pos][0]; check += boundsList[pos][1]; } if ( check < mitk::eps ) { // if all bounds are zero we assume that the bounds could not be correctly determined // and as a fallback generate a time geometry in the old mitk style ProportionalTimeGeometry::Pointer newTimeGeometry = ProportionalTimeGeometry::New(); newTimeGeometry->Initialize( templateGeometry, boundListSize ); timeGeometry = newTimeGeometry.GetPointer(); } else { ArbitraryTimeGeometry::Pointer newTimeGeometry = ArbitraryTimeGeometry::New(); newTimeGeometry->ClearAllGeometries(); newTimeGeometry->ReserveSpaceForGeometries( boundListSize ); for ( std::size_t pos = 0; pos < boundListSize; ++pos ) { TimeBounds bounds = boundsList[pos]; if ( pos + 1 < boundListSize ) { //Currently we do not explicitly support "gaps" in the time coverage //thus we set the max time bound of a time step to the min time bound //of its successor. bounds[1] = boundsList[pos + 1][0]; } newTimeGeometry->AppendNewTimeStepClone(templateGeometry, bounds[0], bounds[1]); } timeGeometry = newTimeGeometry.GetPointer(); } return timeGeometry; }; diff --git a/Modules/DICOMReader/test/mitkDICOMTagPathTest.cpp b/Modules/DICOMReader/test/mitkDICOMTagPathTest.cpp index 937f1f6c66..57c993fcad 100644 --- a/Modules/DICOMReader/test/mitkDICOMTagPathTest.cpp +++ b/Modules/DICOMReader/test/mitkDICOMTagPathTest.cpp @@ -1,240 +1,240 @@ /*=================================================================== 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 "mitkDICOMTagPath.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkDICOMTagPathTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkDICOMTagPathTestSuite); MITK_TEST(DICOMTagPathToPropertyRegEx); MITK_TEST(DICOMTagPathToPersistenceKeyRegEx); MITK_TEST(DICOMTagPathToPersistenceKeyTemplate); MITK_TEST(DICOMTagPathToPersistenceNameTemplate); MITK_TEST(DICOMTagPathToDCMTKSearchPath); MITK_TEST(PropertyNameToDICOMTagPath); MITK_TEST(DICOMTagPathToPropertyName); MITK_TEST(ExecutePropertyRegEx); CPPUNIT_TEST_SUITE_END(); private: mitk::DICOMTagPath simplePath; mitk::DICOMTagPath deepPath; mitk::DICOMTagPath deepPath_withAnyElement; mitk::DICOMTagPath deepPath_withAnySelection; mitk::DICOMTagPath deepPath_withSelection; mitk::DICOMTagPath verydeepPath; mitk::DICOMTagPath emptyPath; public: void setUp() override { simplePath.AddElement(0x0010, 0x0011); deepPath.AddElement(0x0010, 0x0011); deepPath.AddElement(0x0020, 0x0022); deepPath.AddElement(0x003A, 0x0033); deepPath_withAnyElement.AddElement(0x0010, 0x0011); deepPath_withAnyElement.AddAnyElement(); deepPath_withAnyElement.AddElement(0x003a, 0x003f); deepPath_withAnySelection.AddElement(0x0010, 0x0011); deepPath_withAnySelection.AddAnySelection(0x002B, 0x002E); deepPath_withAnySelection.AddElement(0x0030, 0x0033); deepPath_withSelection.AddElement(0x0010, 0x0011); deepPath_withSelection.AddSelection(0x0020, 0x0022, 6); deepPath_withSelection.AddElement(0x003b, 0x003e); verydeepPath.AddAnySelection(0x0010, 0x0011); verydeepPath.AddAnyElement(); verydeepPath.AddElement(0x0030, 0x0033); verydeepPath.AddSelection(0x004c, 0x004d, 4); verydeepPath.AddElement(0x0050, 0x0055); } void tearDown() override { } void DICOMTagPathToPropertyRegEx() { std::string result = mitk::DICOMTagPathToPropertyRegEx(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyRegEx() with '(0010,0011)'", std::string("DICOM\\.0010\\.0011"), result); result = mitk::DICOMTagPathToPropertyRegEx(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyRegEx() with '(0010,0011).(0020,0022).(003A,0033)'", std::string("DICOM\\.0010\\.0011\\.0020\\.0022\\.(003a|003A)\\.0033"), result); result = mitk::DICOMTagPathToPropertyRegEx(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyRegEx() with '(0010,0011).*.(003a,003f)'", std::string("DICOM\\.0010\\.0011\\.([A-Fa-f\\d]{4})\\.([A-Fa-f\\d]{4})\\.(003a|003A)\\.(003f|003F)"), result); result = mitk::DICOMTagPathToPropertyRegEx(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyRegEx() with '(0010,0011).(002B,002E)[*].(0030,0033)'", std::string("DICOM\\.0010\\.0011\\.(002b|002B)\\.(002e|002E)\\.\\[(\\d*)\\]\\.0030\\.0033"), result); result = mitk::DICOMTagPathToPropertyRegEx(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyRegEx() with '(0010,0011).(0020,0022)[6].(003b,003e)'", std::string("DICOM\\.0010\\.0011\\.0020\\.0022\\.\\[6\\]\\.(003b|003B)\\.(003e|003E)"), result); result = mitk::DICOMTagPathToPropertyRegEx(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyRegEx() with '(0010,0011)[*].*.(0030,0033).(004c,004d)[4].(0050,0055)'", std::string("DICOM\\.0010\\.0011\\.\\[(\\d*)\\]\\.([A-Fa-f\\d]{4})\\.([A-Fa-f\\d]{4})\\.0030\\.0033\\.(004c|004C)\\.(004d|004D)\\.\\[4\\]\\.0050\\.0055"), result); } void DICOMTagPathToPersistenceKeyRegEx() { std::string result = mitk::DICOMTagPathToPersistenceKeyRegEx(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyRegEx() with '(0010,0011)'", std::string("DICOM_0010_0011"), result); result = mitk::DICOMTagPathToPersistenceKeyRegEx(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyRegEx() with '(0010,0011).(0020,0022).(003A,0033)'", std::string("DICOM_0010_0011_0020_0022_(003a|003A)_0033"), result); result = mitk::DICOMTagPathToPersistenceKeyRegEx(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyRegEx() with '(0010,0011).*.(003a,003f)'", std::string("DICOM_0010_0011_([A-Fa-f\\d]{4})_([A-Fa-f\\d]{4})_(003a|003A)_(003f|003F)"), result); result = mitk::DICOMTagPathToPersistenceKeyRegEx(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyRegEx() with '(0010,0011).(002B,002E)[*].(0030,0033)'", std::string("DICOM_0010_0011_(002b|002B)_(002e|002E)_\\[(\\d*)\\]_0030_0033"), result); result = mitk::DICOMTagPathToPersistenceKeyRegEx(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyRegEx() with '(0010,0011).(0020,0022)[6].(003b,003e)'", std::string("DICOM_0010_0011_0020_0022_\\[6\\]_(003b|003B)_(003e|003E)"), result); result = mitk::DICOMTagPathToPersistenceKeyRegEx(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyRegEx() with '(0010,0011)[*].*.(0030,0033).(004c,004d)[4].(0050,0055)'", std::string("DICOM_0010_0011_\\[(\\d*)\\]_([A-Fa-f\\d]{4})_([A-Fa-f\\d]{4})_0030_0033_(004c|004C)_(004d|004D)_\\[4\\]_0050_0055"), result); } void DICOMTagPathToPersistenceKeyTemplate() { std::string result = mitk::DICOMTagPathToPersistenceKeyTemplate(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyTemplate() with '(0010,0011)'", std::string("DICOM_0010_0011"), result); result = mitk::DICOMTagPathToPersistenceKeyTemplate(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyTemplate() with '(0010,0011).(0020,0022).(003A,0033)'", std::string("DICOM_0010_0011_0020_0022_003A_0033"), result); result = mitk::DICOMTagPathToPersistenceKeyTemplate(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyTemplate() with '(0010,0011).*.(003a,003f)'", std::string("DICOM_0010_0011_$1_$2_003A_003F"), result); result = mitk::DICOMTagPathToPersistenceKeyTemplate(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyTemplate() with '(0010,0011).(002B,002E)[*].(0030,0033)'", std::string("DICOM_0010_0011_002B_002E_[$1]_0030_0033"), result); result = mitk::DICOMTagPathToPersistenceKeyTemplate(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyTemplate() with '(0010,0011).(0020,0022)[6].(003b,003e)'", std::string("DICOM_0010_0011_0020_0022_[6]_003B_003E"), result); result = mitk::DICOMTagPathToPersistenceKeyTemplate(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceKeyTemplate() with '(0010,0011)[*].*.(0030,0033).(004c,004d)[4].(0050,0055)'", std::string("DICOM_0010_0011_[$1]_$2_$3_0030_0033_004C_004D_[4]_0050_0055"), result); } void DICOMTagPathToPersistenceNameTemplate() { std::string result = mitk::DICOMTagPathToPersistenceNameTemplate(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceNameTemplate() with '(0010,0011)'", std::string("DICOM.0010.0011"), result); result = mitk::DICOMTagPathToPersistenceNameTemplate(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceNameTemplate() with '(0010,0011).(0020,0022).(003A,0033)'", std::string("DICOM.0010.0011.0020.0022.003A.0033"), result); result = mitk::DICOMTagPathToPersistenceNameTemplate(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceNameTemplate() with '(0010,0011).*.(003a,003f)'", std::string("DICOM.0010.0011.$1.$2.003A.003F"), result); result = mitk::DICOMTagPathToPersistenceNameTemplate(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceNameTemplate() with '(0010,0011).(002B,002E)[*].(0030,0033)'", std::string("DICOM.0010.0011.002B.002E.[$1].0030.0033"), result); result = mitk::DICOMTagPathToPersistenceNameTemplate(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceNameTemplate() with '(0010,0011).(0020,0022)[6].(003b,003e)'", std::string("DICOM.0010.0011.0020.0022.[6].003B.003E"), result); result = mitk::DICOMTagPathToPersistenceNameTemplate(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPersistenceNameTemplate() with '(0010,0011)[*].*.(0030,0033).(004c,004d)[4].(0050,0055)'", std::string("DICOM.0010.0011.[$1].$2.$3.0030.0033.004C.004D.[4].0050.0055"), result); } void DICOMTagPathToDCMTKSearchPath() { std::string result = mitk::DICOMTagPathToDCMTKSearchPath(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToDCMTKSearchPath() with '(0010,0011)'", std::string("(0010,0011)"), result); result = mitk::DICOMTagPathToDCMTKSearchPath(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToDCMTKSearchPath() with '(0010,0011).(0020,0022).(003A,0033)'", std::string("(0010,0011).(0020,0022).(003A,0033)"), result); CPPUNIT_ASSERT_THROW(mitk::DICOMTagPathToDCMTKSearchPath(deepPath_withAnyElement), mitk::Exception); result = mitk::DICOMTagPathToDCMTKSearchPath(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToDCMTKSearchPath() with '(0010,0011).(002B,002E)[*].(0030,0033)'", std::string("(0010,0011).(002B,002E)[*].(0030,0033)"), result); result = mitk::DICOMTagPathToDCMTKSearchPath(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToDCMTKSearchPath() with '(0010,0011).(0020,0022)[6].(003b,003e)'", std::string("(0010,0011).(0020,0022)[6].(003B,003E)"), result); CPPUNIT_ASSERT_THROW(mitk::DICOMTagPathToDCMTKSearchPath(verydeepPath), mitk::Exception); } void PropertyNameToDICOMTagPath() { mitk::DICOMTagPath result = mitk::PropertyNameToDICOMTagPath("DICOM.0010.0011"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToDICOMTagPath() with '(0010,0011)'", simplePath, result); result = mitk::PropertyNameToDICOMTagPath("DICOM.0010.0011.0020.0022.003A.0033"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToDICOMTagPath() with '(0010,0011).(0020,0022).(003A,0033)'", deepPath, result); result = mitk::PropertyNameToDICOMTagPath("DICOM.0010.0011.*.003a.003f"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToDICOMTagPath() with '(0010,0011).*.(003a,003f)'", deepPath_withAnyElement, result); result = mitk::PropertyNameToDICOMTagPath("DICOM.0010.0011.002B.002E.[*].0030.0033"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToDICOMTagPath() with '(0010,0011).(002B,002E)[*].(0030,0033)'", deepPath_withAnySelection, result); result = mitk::PropertyNameToDICOMTagPath("DICOM.0010.0011.0020.0022.[6].003b.003e"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToDICOMTagPath() with '(0010,0011).(0020,0022)[6].(003b,003e)'", deepPath_withSelection, result); result = mitk::PropertyNameToDICOMTagPath("DICOM.0010.0011.[*].*.0030.0033.004c.004d.[4].0050.0055"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToDICOMTagPath() with '(0010,0011)[*].*.(0030,0033).(004c,004d)[4].(0050,0055)'", verydeepPath, result); result = mitk::PropertyNameToDICOMTagPath("WRONG.0010.0011.0020.0022.0030.0033"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToDICOMTagPath() with wrong path", emptyPath, result); } void DICOMTagPathToPropertyName() { std::string result = mitk::DICOMTagPathToPropertyName(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyName() with '(0010,0011)'", result, std::string("DICOM.0010.0011")); result = mitk::DICOMTagPathToPropertyName(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyName() with '(0010,0011).(0020,0022).(003A,0033)'", result, std::string("DICOM.0010.0011.0020.0022.003A.0033")); result = mitk::DICOMTagPathToPropertyName(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyName() with '(0010,0011).*.(003a,003f)'", result, std::string("DICOM.0010.0011.*.003A.003F")); result = mitk::DICOMTagPathToPropertyName(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyName() with '(0010,0011).(002B,002E)[*].(0030,0033)'", result, std::string("DICOM.0010.0011.002B.002E.[*].0030.0033")); result = mitk::DICOMTagPathToPropertyName(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyName() with '(0010,0011).(0020,0022)[6].(003b,003e)'", result, std::string("DICOM.0010.0011.0020.0022.[6].003B.003E")); result = mitk::DICOMTagPathToPropertyName(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing DICOMTagPathToPropertyName() with '(0010,0011)[*].*.(0030,0033).(004c,004d)[4].(0050,0055)'", result, std::string("DICOM.0010.0011.[*].*.0030.0033.004C.004D.[4].0050.0055")); } void ExecutePropertyRegEx() { std::regex regEx(mitk::DICOMTagPathToPropertyRegEx(simplePath)); std::string result = mitk::DICOMTagPathToPropertyName(simplePath); CPPUNIT_ASSERT(std::regex_match(result, regEx)); regEx = std::regex(mitk::DICOMTagPathToPropertyRegEx(deepPath)); result = mitk::DICOMTagPathToPropertyName(deepPath); CPPUNIT_ASSERT(std::regex_match(result, regEx)); regEx = std::regex(mitk::DICOMTagPathToPropertyRegEx(deepPath_withAnyElement)); result = mitk::DICOMTagPathToPropertyName(deepPath_withAnyElement); - int position = result.find("*"); + auto position = result.find("*"); if (std::string::npos != position) { result.replace(position, 1, "1234.ABCD"); CPPUNIT_ASSERT(std::regex_match(result, regEx)); } regEx = std::regex(mitk::DICOMTagPathToPropertyRegEx(deepPath_withAnySelection)); result = mitk::DICOMTagPathToPropertyName(deepPath_withAnySelection); position = result.find("[*]"); if (std::string::npos != position) { result.replace(position, 3, "[10]"); CPPUNIT_ASSERT(std::regex_match(result, regEx)); } regEx = std::regex(mitk::DICOMTagPathToPropertyRegEx(deepPath_withSelection)); result = mitk::DICOMTagPathToPropertyName(deepPath_withSelection); CPPUNIT_ASSERT(std::regex_match(result, regEx)); regEx = std::regex(mitk::DICOMTagPathToPropertyRegEx(verydeepPath)); result = mitk::DICOMTagPathToPropertyName(verydeepPath); position = result.find("[*]"); if (std::string::npos != position) { result.replace(position, 3, "[1]"); position = result.find("*"); if (std::string::npos != position) { result.replace(position, 1, "abcd.1234"); CPPUNIT_ASSERT(std::regex_match(result, regEx)); } } } }; MITK_TEST_SUITE_REGISTRATION(mitkDICOMTagPath) diff --git a/Modules/DICOMReaderServices/src/mitkClassicDICOMSeriesReaderService.cpp b/Modules/DICOMReaderServices/src/mitkClassicDICOMSeriesReaderService.cpp index b8d137c7d5..8d70e81130 100644 --- a/Modules/DICOMReaderServices/src/mitkClassicDICOMSeriesReaderService.cpp +++ b/Modules/DICOMReaderServices/src/mitkClassicDICOMSeriesReaderService.cpp @@ -1,41 +1,41 @@ /*=================================================================== 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 "mitkClassicDICOMSeriesReaderService.h" #include namespace mitk { ClassicDICOMSeriesReaderService::ClassicDICOMSeriesReaderService() : BaseDICOMReaderService("MITK DICOM Reader v2 (classic config)") { this->RegisterService(); } - DICOMFileReader::Pointer ClassicDICOMSeriesReaderService::GetReader(const mitk::StringList& relevantFiles) const + DICOMFileReader::Pointer ClassicDICOMSeriesReaderService::GetReader(const mitk::StringList&) const { mitk::ClassicDICOMSeriesReader::Pointer reader = mitk::ClassicDICOMSeriesReader::New(); return reader.GetPointer(); }; ClassicDICOMSeriesReaderService* ClassicDICOMSeriesReaderService::Clone() const { return new ClassicDICOMSeriesReaderService(*this); } } diff --git a/Modules/DicomUI/src/QmitkDicomExternalDataWidget.cpp b/Modules/DicomUI/src/QmitkDicomExternalDataWidget.cpp index 4500968fdc..5ed4efc0cb 100644 --- a/Modules/DicomUI/src/QmitkDicomExternalDataWidget.cpp +++ b/Modules/DicomUI/src/QmitkDicomExternalDataWidget.cpp @@ -1,212 +1,212 @@ /*=================================================================== 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. ===================================================================*/ // Qmitk #include "QmitkDicomExternalDataWidget.h" #include // CTK #include // Qt #include #include const std::string QmitkDicomExternalDataWidget::Widget_ID = "org.mitk.Widgets.QmitkDicomExternalDataWidget"; QmitkDicomExternalDataWidget::QmitkDicomExternalDataWidget(QWidget *parent) - : QWidget(parent), m_Controls(nullptr), m_ProgressDialog(nullptr) + : QWidget(parent), m_ProgressDialog(nullptr), m_Controls(nullptr) { Initialize(); CreateQtPartControl(this); } QmitkDicomExternalDataWidget::~QmitkDicomExternalDataWidget() { } void QmitkDicomExternalDataWidget::CreateQtPartControl(QWidget *parent) { // build up qt Widget, unless already done if (!m_Controls) { // create GUI widgets from the Qt Designer's .ui file m_Controls = new Ui::QmitkDicomExternalDataWidgetControls; m_Controls->setupUi(parent); m_Controls->viewExternalDataButton->setVisible(true); m_Controls->ctkDICOMBrowser->setTableOrientation(Qt::Vertical); m_Controls->ctkDICOMBrowser->setDICOMDatabase(m_ExternalDatabase); SetupImportDialog(); SetupProgressDialog(); // connect buttons connect(m_Controls->downloadButton, SIGNAL(clicked()), this, SLOT(OnDownloadButtonClicked())); connect(m_Controls->viewExternalDataButton, SIGNAL(clicked()), this, SLOT(OnViewButtonClicked())); connect(m_Controls->directoryButton, SIGNAL(clicked()), m_ImportDialog, SLOT(show())); connect(m_Controls->ctkDICOMBrowser, SIGNAL(seriesSelectionChanged(const QStringList &)), this, SLOT(OnSeriesSelectionChanged(const QStringList &))); connect( m_Controls->ctkDICOMBrowser, SIGNAL(seriesDoubleClicked(const QModelIndex &)), this, SLOT(OnViewButtonClicked())); connect(m_ImportDialog, SIGNAL(fileSelected(QString)), this, SLOT(OnStartDicomImport(QString))); connect(m_ExternalIndexer, SIGNAL(indexingFilePath(const QString &)), m_ProgressDialog, SLOT(setLabelText(const QString &))); connect(m_ExternalIndexer, SIGNAL(progress(int)), m_ProgressDialog, SLOT(setValue(int))); // actually the progress dialog closes if the maximum value is reached, BUT // the following line is needed since the external indexer wont reach maximum value (100 % progress) connect(m_ExternalIndexer, SIGNAL(indexingComplete()), m_ProgressDialog, SLOT(close())); connect(m_ProgressDialog, SIGNAL(canceled()), m_ExternalIndexer, SLOT(cancel())); } } void QmitkDicomExternalDataWidget::Initialize() { m_ExternalDatabase = new ctkDICOMDatabase(this); try { m_ExternalDatabase->openDatabase(QString(":memory:"), QString("EXTERNAL-DB")); } catch (const std::exception&) { MITK_ERROR << "Database error: " << m_ExternalDatabase->lastError().toStdString(); m_ExternalDatabase->closeDatabase(); return; } m_ExternalIndexer = new ctkDICOMIndexer(this); } void QmitkDicomExternalDataWidget::OnDownloadButtonClicked() { QStringList filesToDownload = GetFileNamesFromIndex(); if (filesToDownload.size() == 0) { QMessageBox info; info.setText("You have to select an entry in the DICOM browser for import."); info.exec(); return; } emit SignalStartDicomImport(GetFileNamesFromIndex()); } void QmitkDicomExternalDataWidget::OnViewButtonClicked() { QStringList uids = m_Controls->ctkDICOMBrowser->currentSeriesSelection(); QString uid; foreach (uid, uids) { QStringList filesForSeries = m_ExternalDatabase->filesForSeries(uid); QHash eventProperty; eventProperty.insert("FilesForSeries", filesForSeries); if (!filesForSeries.isEmpty()) { QString modality = m_ExternalDatabase->fileValue(filesForSeries.at(0), "0008,0060"); eventProperty.insert("Modality", modality); } emit SignalDicomToDataManager(eventProperty); } } QStringList QmitkDicomExternalDataWidget::GetFileNamesFromIndex() { QStringList filePaths; QString uid; QStringList seriesUIDs = m_Controls->ctkDICOMBrowser->currentSeriesSelection(); foreach (uid, seriesUIDs) { filePaths.append(m_ExternalDatabase->filesForSeries(uid)); } if (!filePaths.empty()) return filePaths; QStringList studyUIDs = m_Controls->ctkDICOMBrowser->currentStudiesSelection(); foreach (uid, studyUIDs) { seriesUIDs = m_ExternalDatabase->seriesForStudy(uid); foreach (uid, seriesUIDs) { filePaths.append(m_ExternalDatabase->filesForSeries(uid)); } } if (!filePaths.empty()) return filePaths; QStringList patientsUIDs = m_Controls->ctkDICOMBrowser->currentPatientsSelection(); foreach (uid, patientsUIDs) { studyUIDs = m_ExternalDatabase->studiesForPatient(uid); foreach (uid, studyUIDs) { seriesUIDs = m_ExternalDatabase->seriesForStudy(uid); foreach (uid, seriesUIDs) { filePaths.append(m_ExternalDatabase->filesForSeries(uid)); } } } return filePaths; } void QmitkDicomExternalDataWidget::OnStartDicomImport(const QString &directory) { m_ImportDialog->close(); // no need to show / start the progress dialog, as the dialog // appears by receiving the progress signal from the external indexer m_LastImportDirectory = directory; m_ExternalIndexer->addDirectory(*m_ExternalDatabase, m_LastImportDirectory); } void QmitkDicomExternalDataWidget::OnSeriesSelectionChanged(const QStringList &s) { m_Controls->viewExternalDataButton->setEnabled((s.size() != 0)); } void QmitkDicomExternalDataWidget::SetupImportDialog() { // Initialize import widget m_ImportDialog = new ctkFileDialog(this); // Since copy on import is not working at the moment // this feature is disabled // QCheckBox* importCheckbox = new QCheckBox("Copy on import", m_ImportDialog); // m_ImportDialog->setBottomWidget(importCheckbox); m_ImportDialog->setFileMode(QFileDialog::Directory); m_ImportDialog->setLabelText(QFileDialog::Accept, "Import"); m_ImportDialog->setWindowTitle("Import DICOM files from directory"); m_ImportDialog->setWindowModality(Qt::ApplicationModal); } void QmitkDicomExternalDataWidget::SetupProgressDialog() { m_ProgressDialog = new QProgressDialog("Initialization ...", "Cancel", 0, 100, this); m_ProgressDialog->setWindowTitle("DICOM Import"); m_ProgressDialog->setWindowModality(Qt::ApplicationModal); m_ProgressDialog->setMinimumDuration(0); // FIX T20008: immediately set the progress dialog value to maximum --> will close the dialog m_ProgressDialog->setValue(100); } diff --git a/Modules/ImageDenoising/itkLocalVariationImageFilter.txx b/Modules/ImageDenoising/itkLocalVariationImageFilter.txx index 2cfa5e98d6..3048232a6c 100644 --- a/Modules/ImageDenoising/itkLocalVariationImageFilter.txx +++ b/Modules/ImageDenoising/itkLocalVariationImageFilter.txx @@ -1,193 +1,193 @@ /*=================================================================== 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. ===================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef _itkLocalVariationImageFilter_txx #define _itkLocalVariationImageFilter_txx #include "itkLocalVariationImageFilter.h" #include "itkConstShapedNeighborhoodIterator.h" #include "itkImageRegionIterator.h" #include "itkNeighborhoodAlgorithm.h" #include "itkNeighborhoodInnerProduct.h" #include "itkOffset.h" #include "itkProgressReporter.h" #include "itkVectorImage.h" #include "itkZeroFluxNeumannBoundaryCondition.h" #include #include namespace itk { template LocalVariationImageFilter::LocalVariationImageFilter() { } template void LocalVariationImageFilter::GenerateInputRequestedRegion() throw( InvalidRequestedRegionError) { // call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); // get pointers to the input and output typename Superclass::InputImagePointer inputPtr = const_cast(this->GetInput()); typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); if (!inputPtr || !outputPtr) { return; } // get a copy of the input requested region (should equal the output // requested region) typename TInputImage::RegionType inputRequestedRegion; inputRequestedRegion = inputPtr->GetRequestedRegion(); // pad the input requested region by 1 inputRequestedRegion.PadByRadius(1); // crop the input requested region at the input's largest possible region if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion())) { inputPtr->SetRequestedRegion(inputRequestedRegion); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) inputPtr->SetRequestedRegion(inputRequestedRegion); // build an exception InvalidRequestedRegionError e(__FILE__, __LINE__); e.SetLocation(ITK_LOCATION); e.SetDescription("Requested region outside possible region."); e.SetDataObject(inputPtr); throw e; } } template <> double SquaredEuclideanMetric>::Calc(itk::VariableLengthVector p) { return p.GetSquaredNorm(); } template <> double SquaredEuclideanMetric>::Calc(itk::VariableLengthVector p) { return p.GetSquaredNorm(); } template double SquaredEuclideanMetric::Calc(TPixelType p) { return p * p; } template void LocalVariationImageFilter::ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType threadId) { // Allocate output typename OutputImageType::Pointer output = this->GetOutput(); typename InputImageType::ConstPointer input = this->GetInput(); itk::Size size; - for (int i = 0; i < InputImageDimension; i++) + for (unsigned int i = 0; i < InputImageDimension; i++) size[i] = 1; // Find the data-set boundary "faces" NeighborhoodAlgorithm::ImageBoundaryFacesCalculator bC; typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator::FaceListType faceList = bC(input, outputRegionForThread, size); // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels()); ZeroFluxNeumannBoundaryCondition nbc; std::vector pixels; // Process each of the boundary faces. These are N-d regions which border // the edge of the buffer. for (auto fit = faceList.begin(); fit != faceList.end(); ++fit) { // iterators over output and input ImageRegionIterator output_image_it(output, *fit); ImageRegionConstIterator input_image_it(input.GetPointer(), *fit); // neighborhood iterator for input image ConstShapedNeighborhoodIterator input_image_neighbors_it(size, input, *fit); typename ConstShapedNeighborhoodIterator::OffsetType offset; input_image_neighbors_it.OverrideBoundaryCondition(&nbc); input_image_neighbors_it.ClearActiveList(); - for (int i = 0; i < InputImageDimension; i++) + for (unsigned int i = 0; i < InputImageDimension; i++) { offset.Fill(0); offset[i] = -1; input_image_neighbors_it.ActivateOffset(offset); offset[i] = 1; input_image_neighbors_it.ActivateOffset(offset); } input_image_neighbors_it.GoToBegin(); // const unsigned int neighborhoodSize = InputImageDimension*2; while (!input_image_neighbors_it.IsAtEnd()) { // collect all the pixels in the neighborhood, note that we use // GetPixel on the NeighborhoodIterator to honor the boundary conditions typename OutputImageType::PixelType locVariation = 0; typename ConstShapedNeighborhoodIterator::ConstIterator input_neighbors_it; for (input_neighbors_it = input_image_neighbors_it.Begin(); !input_neighbors_it.IsAtEnd(); input_neighbors_it++) { typename TInputImage::PixelType diffVec = input_neighbors_it.Get() - input_image_it.Get(); locVariation += SquaredEuclideanMetric::Calc(diffVec); } locVariation = sqrt(locVariation + 0.0001); output_image_it.Set(locVariation); // update iterators ++input_image_neighbors_it; ++output_image_it; ++input_image_it; // report progress progress.CompletedPixel(); } } } /** * Standard "PrintSelf" method */ template void LocalVariationImageFilter::PrintSelf(std::ostream &os, Indent indent) const { Superclass::PrintSelf(os, indent); } } // end namespace itk #endif //_itkLocalVariationImageFilter_txx diff --git a/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx b/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx index 74e988d84e..13bd039e27 100644 --- a/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx +++ b/Modules/ImageDenoising/itkTotalVariationSingleIterationImageFilter.txx @@ -1,256 +1,256 @@ /*=================================================================== 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. ===================================================================*/ /*=================================================================== This file is based heavily on a corresponding ITK filter. ===================================================================*/ #ifndef _itkTotalVariationSingleIterationImageFilter_txx #define _itkTotalVariationSingleIterationImageFilter_txx #include "itkTotalVariationSingleIterationImageFilter.h" // itk includes #include "itkConstShapedNeighborhoodIterator.h" #include "itkImageRegionIterator.h" #include "itkLocalVariationImageFilter.h" #include "itkNeighborhoodAlgorithm.h" #include "itkNeighborhoodInnerProduct.h" #include "itkOffset.h" #include "itkProgressReporter.h" #include "itkZeroFluxNeumannBoundaryCondition.h" // other includes #include #include namespace itk { /** * constructor */ template TotalVariationSingleIterationImageFilter::TotalVariationSingleIterationImageFilter() { m_Lambda = 1.0; m_LocalVariation = LocalVariationImageType::New(); } /** * generate requested region */ template void TotalVariationSingleIterationImageFilter::GenerateInputRequestedRegion() throw( InvalidRequestedRegionError) { // call the superclass' implementation of this method Superclass::GenerateInputRequestedRegion(); // get pointers to the input and output typename Superclass::InputImagePointer inputPtr = const_cast(this->GetInput()); typename Superclass::OutputImagePointer outputPtr = this->GetOutput(); if (!inputPtr || !outputPtr) { return; } // get a copy of the input requested region (should equal the output // requested region) typename TInputImage::RegionType inputRequestedRegion; inputRequestedRegion = inputPtr->GetRequestedRegion(); // pad the input requested region by 1 inputRequestedRegion.PadByRadius(1); // crop the input requested region at the input's largest possible region if (inputRequestedRegion.Crop(inputPtr->GetLargestPossibleRegion())) { inputPtr->SetRequestedRegion(inputRequestedRegion); return; } else { // Couldn't crop the region (requested region is outside the largest // possible region). Throw an exception. // store what we tried to request (prior to trying to crop) inputPtr->SetRequestedRegion(inputRequestedRegion); // build an exception InvalidRequestedRegionError e(__FILE__, __LINE__); e.SetLocation(ITK_LOCATION); e.SetDescription("Requested region outside possible region."); e.SetDataObject(inputPtr); throw e; } } /** * generate output */ template void TotalVariationSingleIterationImageFilter::ThreadedGenerateData( const OutputImageRegionType &outputRegionForThread, ThreadIdType threadId) { typename OutputImageType::Pointer output = this->GetOutput(); typename InputImageType::ConstPointer input = this->GetInput(); // Find the data-set boundary "faces" itk::Size size; - for (int i = 0; i < InputImageDimension; i++) + for (unsigned int i = 0; i < InputImageDimension; i++) size[i] = 1; NeighborhoodAlgorithm::ImageBoundaryFacesCalculator bC; typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator::FaceListType faceList = bC(input, outputRegionForThread, size); NeighborhoodAlgorithm::ImageBoundaryFacesCalculator lv_bC; typename NeighborhoodAlgorithm::ImageBoundaryFacesCalculator::FaceListType lv_faceList = lv_bC(m_LocalVariation, outputRegionForThread, size); // support progress methods/callbacks ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels()); ZeroFluxNeumannBoundaryCondition nbc; ZeroFluxNeumannBoundaryCondition lv_nbc; std::vector ws; std::vector hs; auto lv_fit = lv_faceList.begin(); // Process each of the boundary faces. These are N-d regions which border // the edge of the buffer. for (auto fit = faceList.begin(); fit != faceList.end(); ++fit) { // iterators over output, input, original and local variation image ImageRegionIterator output_image_it = ImageRegionIterator(output, *fit); ImageRegionConstIterator input_image_it = ImageRegionConstIterator(input, *fit); ImageRegionConstIterator orig_image_it = ImageRegionConstIterator(m_OriginalImage, *fit); ImageRegionConstIterator loc_var_image_it = ImageRegionConstIterator(m_LocalVariation, *fit); // neighborhood in input image ConstShapedNeighborhoodIterator input_image_neighbors_it(size, input, *fit); typename ConstShapedNeighborhoodIterator::OffsetType offset; input_image_neighbors_it.OverrideBoundaryCondition(&nbc); input_image_neighbors_it.ClearActiveList(); - for (int i = 0; i < InputImageDimension; i++) + for (unsigned int i = 0; i < InputImageDimension; i++) { offset.Fill(0); offset[i] = -1; input_image_neighbors_it.ActivateOffset(offset); offset[i] = 1; input_image_neighbors_it.ActivateOffset(offset); } input_image_neighbors_it.GoToBegin(); // neighborhood in local variation image ConstShapedNeighborhoodIterator loc_var_image_neighbors_it( size, m_LocalVariation, *lv_fit); loc_var_image_neighbors_it.OverrideBoundaryCondition(&lv_nbc); loc_var_image_neighbors_it.ClearActiveList(); - for (int i = 0; i < InputImageDimension; i++) + for (unsigned int i = 0; i < InputImageDimension; i++) { offset.Fill(0); offset[i] = -1; loc_var_image_neighbors_it.ActivateOffset(offset); offset[i] = 1; loc_var_image_neighbors_it.ActivateOffset(offset); } loc_var_image_neighbors_it.GoToBegin(); const unsigned int neighborhoodSize = InputImageDimension * 2; ws.resize(neighborhoodSize); while (!output_image_it.IsAtEnd()) { // 1 / ||nabla_alpha(u)||_a double locvar_alpha_inv = 1.0 / loc_var_image_it.Get(); // compute w_alphabetas int count = 0; double wsum = 0; typename ConstShapedNeighborhoodIterator::ConstIterator loc_var_neighbors_it; for (loc_var_neighbors_it = loc_var_image_neighbors_it.Begin(); !loc_var_neighbors_it.IsAtEnd(); loc_var_neighbors_it++) { // w_alphabeta(u) = // 1 / ||nabla_alpha(u)||_a + 1 / ||nabla_beta(u)||_a ws[count] = locvar_alpha_inv + (1.0 / (double)loc_var_neighbors_it.Get()); wsum += ws[count++]; } // h_alphaalpha * u_alpha^zero typename OutputImageType::PixelType res = static_cast( ((typename OutputImageType::PixelType)orig_image_it.Get()) * (m_Lambda / (m_Lambda + wsum))); // add the different h_alphabeta * u_beta count = 0; typename ConstShapedNeighborhoodIterator::ConstIterator input_neighbors_it; for (input_neighbors_it = input_image_neighbors_it.Begin(); !input_neighbors_it.IsAtEnd(); input_neighbors_it++) { res += input_neighbors_it.Get() * (ws[count++] / (m_Lambda + wsum)); } // set output result output_image_it.Set(res); // increment iterators ++output_image_it; ++input_image_it; ++orig_image_it; ++loc_var_image_it; ++input_image_neighbors_it; ++loc_var_image_neighbors_it; // report progress progress.CompletedPixel(); } ++lv_fit; } } /** * first calculate local variation in the image */ template void TotalVariationSingleIterationImageFilter::BeforeThreadedGenerateData() { typedef typename itk::LocalVariationImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); filter->SetInput(this->GetInput(0)); filter->SetNumberOfThreads(this->GetNumberOfThreads()); filter->Update(); this->m_LocalVariation = filter->GetOutput(); } /** * Standard "PrintSelf" method */ template void TotalVariationSingleIterationImageFilter::PrintSelf(std::ostream &os, Indent indent) const { Superclass::PrintSelf(os, indent); } } // end namespace itk #endif diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp index 11f751fa84..039239e967 100644 --- a/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp +++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsTextureAnalysisTest.cpp @@ -1,235 +1,234 @@ /*=================================================================== 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(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/itkMultiGaussianImageSource.hxx b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx index 91d77316c1..71a4da4114 100644 --- a/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx +++ b/Modules/ImageStatistics/itkMultiGaussianImageSource.hxx @@ -1,1004 +1,1003 @@ /*========================================================================= * * Copyright Insight Software Consortium * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * *=========================================================================*/ /*========================================================================= * * Portions of this file are subject to the VTK Toolkit Version 3 copyright. * * Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen * * For complete copyright, license and disclaimer of warranty information * please refer to the NOTICE file at the top of the ITK source tree. * *=========================================================================*/ #ifndef __itkMultiGaussianImageSource_hxx #define __itkMultiGaussianImageSource_hxx #include #include #include #include "itkMultiGaussianImageSource.h" #include "itkImageRegionIterator.h" #include "itkObjectFactory.h" #include "itkProgressReporter.h" #include "itkDOMNodeXMLWriter.h" #include "stdlib.h" namespace itk { /** * */ template< class TOutputImage > MultiGaussianImageSource< TOutputImage > ::MultiGaussianImageSource() { //Initial image is 100 wide in each direction. for ( unsigned int i = 0; i < TOutputImage::GetImageDimension(); i++ ) { m_Size[i] = 100; m_Spacing[i] = 1.0; m_Origin[i] = 0.0; m_SphereMidpoint[i] = 0; } m_NumberOfGaussians = 0; m_Radius = 1; m_RadiusStepNumber = 5; m_MeanValue = 0; m_Min = NumericTraits< OutputImagePixelType >::NonpositiveMin(); m_Max = NumericTraits< OutputImagePixelType >::max(); } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > MultiGaussianImageSource< TOutputImage > ::~MultiGaussianImageSource() {} //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetSize(SizeValueArrayType sizeArray) { const unsigned int count = TOutputImage::ImageDimension; unsigned int i; for ( i = 0; i < count; i++ ) { if ( sizeArray[i] != this->m_Size[i] ) { break; } } if ( i < count ) { this->Modified(); for ( i = 0; i < count; i++ ) { this->m_Size[i] = sizeArray[i]; } } } template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::SizeValueType * MultiGaussianImageSource< TOutputImage > ::GetSize() const { return this->m_Size.GetSize(); } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetSpacing(SpacingValueArrayType spacingArray) { const unsigned int count = TOutputImage::ImageDimension; unsigned int i; for ( i = 0; i < count; i++ ) { if ( spacingArray[i] != this->m_Spacing[i] ) { break; } } if ( i < count ) { this->Modified(); for ( i = 0; i < count; i++ ) { this->m_Spacing[i] = spacingArray[i]; } } } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetOrigin(PointValueArrayType originArray) { const unsigned int count = TOutputImage::ImageDimension; unsigned int i; for ( i = 0; i < count; i++ ) { if ( originArray[i] != this->m_Origin[i] ) { break; } } if ( i < count ) { this->Modified(); for ( i = 0; i < count; i++ ) { this->m_Origin[i] = originArray[i]; } } } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::PointValueType * MultiGaussianImageSource< TOutputImage > ::GetOrigin() const { for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ ) { this->m_OriginArray[i] = this->m_Origin[i]; } return this->m_OriginArray; } template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::SpacingValueType * MultiGaussianImageSource< TOutputImage > ::GetSpacing() const { for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ ) { this->m_SpacingArray[i] = this->m_Spacing[i]; } return this->m_SpacingArray; } //----------------------------------------------------------------------------------------------------------------------- /** * */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::PrintSelf(std::ostream & os, Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Max: " << static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_Max ) << std::endl; os << indent << "Min: " << static_cast< typename NumericTraits< OutputImagePixelType >::PrintType >( m_Min ) << std::endl; os << indent << "Origin: ["; unsigned int ii = 0; while( ii < TOutputImage::ImageDimension - 1 ) { os << m_Origin[ii] << ", "; ++ii; } os << m_Origin[ii] << "]" << std::endl; os << indent << "Spacing: ["; ii = 0; while( ii < TOutputImage::ImageDimension - 1 ) { os << m_Spacing[ii] << ", "; ++ii; } os << m_Spacing[ii] << "]" << std::endl; os << indent << "Size: ["; ii = 0; while( ii < TOutputImage::ImageDimension - 1 ) { os << m_Size[ii] << ", "; ++ii; } os << m_Size[ii] << "]" << std::endl; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > unsigned int MultiGaussianImageSource< TOutputImage > ::GetNumberOfGaussians() const { return this->m_NumberOfGaussians; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > typename MultiGaussianImageSource< TOutputImage >::RadiusType MultiGaussianImageSource< TOutputImage > ::GetRadius() const { return this->m_Radius; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetRadius( RadiusType radius ) { this->m_Radius = radius; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetNumberOfGausssians( unsigned int n ) { this->m_NumberOfGaussians = n; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetRegionOfInterest( ItkVectorType roiMin, ItkVectorType roiMax ) { m_RegionOfInterestMax = roiMax; m_RegionOfInterestMin = roiMin; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType MultiGaussianImageSource< TOutputImage > ::GetMaxMeanValue() const { return m_MeanValue; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType MultiGaussianImageSource< TOutputImage > ::GetMaxValueInSphere() const { return m_MaxValueInSphere; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::IndexType MultiGaussianImageSource< TOutputImage > ::GetMaxValueIndexInSphere() const { return m_MaxValueIndexInSphere; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType MultiGaussianImageSource< TOutputImage > ::GetMinValueInSphere() const { return m_MinValueInSphere; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::IndexType MultiGaussianImageSource< TOutputImage > ::GetMinValueIndexInSphere() const { return m_MinValueIndexInSphere; } //----------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > const typename MultiGaussianImageSource< TOutputImage >::IndexType MultiGaussianImageSource< TOutputImage > ::GetSphereMidpoint() const { return m_SphereMidpoint; } //----------------------------------------------------------------------------------------------------------------------- /* Calculate and return value of the integral of the gaussian in a cuboid region with the dimension 3: in the x-axis between xMin and xMax and in the y-axis between yMin and yMax and in the z-axis also between zMin and zMax. */ template< class TOutputImage > double MultiGaussianImageSource< TOutputImage > ::MultiGaussianFunctionValueAtCuboid(double xMin, double xMax, double yMin, double yMax, double zMin, double zMax) { double mean = 0; double summand0, summand1, summand2, value, factor; for(unsigned int n = 0; n < m_NumberOfGaussians; ++n) { summand0 = FunctionPhi((xMax - m_CenterX[n]) / m_SigmaX[n] ) - FunctionPhi((xMin - m_CenterX[n]) / m_SigmaX[n] ); summand1 = FunctionPhi((yMax - m_CenterY[n]) / m_SigmaY[n] ) - FunctionPhi((yMin - m_CenterY[n]) / m_SigmaY[n] ); summand2 = FunctionPhi((zMax - m_CenterZ[n]) / m_SigmaZ[n] ) - FunctionPhi((zMin - m_CenterZ[n]) / m_SigmaZ[n] ); value = summand0 * summand1 * summand2; factor = (m_SigmaX[n] * m_SigmaY[n] * m_SigmaZ[n] ) * pow(2.0 * itk::Math::pi, 1.5 ); mean = mean + factor * value * m_Altitude[n]; } return mean; } //--------------------------------------------------------------------------------------------------------------------- /* Returns the linear interpolation of the values of the standard normal distribution function. This values could be seen in the vector m_NormalDistValues. */ template< class TOutputImage > double MultiGaussianImageSource< TOutputImage > ::FunctionPhi(double value) { double phiAtValue; //linear interpolation between the values int indexValue = static_cast( 100 * value); if( indexValue > 409 ) { return phiAtValue = 1.0; } else if( indexValue < -409 ) { return phiAtValue = 0.0; } else if( indexValue == 409 ) { return phiAtValue = m_NormalDistValues[409]; } else if( indexValue == -409 ) { return phiAtValue = 1.0 - m_NormalDistValues[409]; } else { bool negative = false; if (indexValue < 0.0) { negative = true; value = -value; } int indexUp = static_cast( 100 * value) + 1; int indexDown = static_cast( 100 * value); double alpha = 100.0 * value - static_cast(indexDown); phiAtValue = (1.0 - alpha) * m_NormalDistValues[indexDown] + alpha * m_NormalDistValues[indexUp] ; if(negative) { phiAtValue = 1.0 - phiAtValue; } return phiAtValue; } } //---------------------------------------------------------------------------------------------------------------------- /* Set the midpoint of the cuboid in a vector m_Midpoints. This cuboids discretise the sphere with the octree method. Set the radius of the cuboid ( = 0.5 * length of the side of the cuboid ) in the vector m_RadiusCuboid. */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::InsertPoints( PointType globalCoordinateMidpointCuboid, double cuboidRadius) { typename MapContainerPoints::ElementIdentifier id = m_Midpoints.Size(); m_Midpoints.InsertElement(id, globalCoordinateMidpointCuboid); m_RadiusCuboid.InsertElement(id, cuboidRadius); } //---------------------------------------------------------------------------------------------------------------------- /* This recursive method realise the octree method. It subdivide a cuboid in eight cuboids, when this cuboid crosses the boundary of sphere. If the cuboid is inside the sphere, it calculates the integral and, if uncommented, insert the midpoint of the cuboid in the m_Midpoints vector. */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::CalculateEdgesInSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius, int level ) { double xMin, xMax, yMin, yMax, zMin, zMax; double cuboidRadiusRecursion = cuboidRadius; PointType newMidpoint; int intersect = IntersectTheSphere( globalCoordinateMidpointCuboid, globalCoordinateMidpointSphere, cuboidRadiusRecursion); if( intersect == 1 ) { if (level < 4) { // cuboid intersect the sphere -> call CalculateEdgesInSphere (this method) for the subdivided cuboid cuboidRadiusRecursion = cuboidRadiusRecursion / 2.0; for(int i = -1; i < 2; i+=2) { for(int k = -1; k < 2; k+=2) { for(int j = -1; j < 2; j+=2) { newMidpoint[0] = globalCoordinateMidpointCuboid[0] + static_cast(i) * cuboidRadiusRecursion; newMidpoint[1] = globalCoordinateMidpointCuboid[1] + static_cast(k) * cuboidRadiusRecursion; newMidpoint[2] = globalCoordinateMidpointCuboid[2] + static_cast(j) * cuboidRadiusRecursion; this->CalculateEdgesInSphere( newMidpoint, globalCoordinateMidpointSphere, cuboidRadiusRecursion, level + 1 ); } } } } // last step of recursion -> on the boundary else { // Calculate the integral and take the half of it (because we are on the boundary) xMin = globalCoordinateMidpointCuboid[0] - cuboidRadius; xMax = globalCoordinateMidpointCuboid[0] + cuboidRadius; yMin = globalCoordinateMidpointCuboid[1] - cuboidRadius; yMax = globalCoordinateMidpointCuboid[1] + cuboidRadius; zMin = globalCoordinateMidpointCuboid[2] - cuboidRadius; zMax = globalCoordinateMidpointCuboid[2] + cuboidRadius; // size is in index coordinate -> multiply by the spacing -> global coordinate of the image boundary // yz Plane bool yzPlaneNotCrossYSection = xMin >= m_Origin[0] && xMax <= m_Size[0] * m_Spacing[0]; // xz Plane bool xzPlaneNotCrossYSection = yMin >= m_Origin[1] && yMax <= m_Size[1] * m_Spacing[1]; // xy Plane bool xyPlaneNotCrossZSection = zMin >= m_Origin[2] && zMax <= m_Size[2] * m_Spacing[2]; //check if the boundary of the integral is inside the image if( yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) { //double temp = this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ) * 0.5; m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ) * 0.5; m_Volume = m_Volume + pow( 2.0 * cuboidRadius, 3.0) / 2.0; } } } else if(intersect == 2) { // cuboid in the sphere // To insert the midpoint and the radius of the cuboid in a vector, so that we can visualise the midpoints, uncomment the next line and the line WriteXMLToTestTheCuboidInsideTheSphere(); // InsertPoints(globalCoordinateMidpointCuboid, cuboidRadius); // Calculate the integral boundary xMin = globalCoordinateMidpointCuboid[0] - cuboidRadius; xMax = globalCoordinateMidpointCuboid[0] + cuboidRadius; yMin = globalCoordinateMidpointCuboid[1] - cuboidRadius; yMax = globalCoordinateMidpointCuboid[1] + cuboidRadius; zMin = globalCoordinateMidpointCuboid[2] - cuboidRadius; zMax = globalCoordinateMidpointCuboid[2] + cuboidRadius; // size is in index coordinate -> multiply by the spacing -> global coordinate of the image boundary // yz Plane // bool yzPlaneAtOriginCrossXSection = xMin <= m_Origin[0] && xMax >= m_Origin[0]; // bool yzPlaneAtImageBorderCrossXSection = xMin <= m_Size[0] * m_Spacing[0] && xMax >= m_Size[0] * m_Spacing[0]; bool yzPlaneNotCrossYSection = xMin >= m_Origin[0] && xMax <= m_Size[0] * m_Spacing[0]; // xz Plane // bool xzPlaneAtOriginCrossYSection = yMin <= m_Origin[1] && yMax >= m_Origin[1]; // bool xzPlaneAtImageBorderCrossYSection = yMin <= m_Size[1] * m_Spacing[1] && yMax >= m_Size[1] * m_Spacing[1]; bool xzPlaneNotCrossYSection = yMin >= m_Origin[1] && yMax <= m_Size[1] * m_Spacing[1]; // xy Plane // bool xyPlaneAtOriginCrossZSection = zMin <= m_Origin[2] && zMax >= m_Origin[2]; // bool xyPlaneAtImageBorderCrossZSection = zMin <= m_Size[2] * m_Spacing[2] && zMax >= m_Size[2] * m_Spacing[2]; bool xyPlaneNotCrossZSection = zMin >= m_Origin[2] && zMax <= m_Size[2] * m_Spacing[2]; if( yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) { m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ); m_Volume = m_Volume + pow( 2.0 * cuboidRadius, 3.0); } // check if the boundary of the image intersect the cuboid and if yes, change the limits of the cuboid to be only inside the image; therefor we cut the sphere and neglect the part of it outside the image else /* if( // one plane crosses the cuboid ( (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) ) || // two plane cross the cuboid (on the image edges possible) ( (yzPlaneAtOriginCrossXSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) || ( (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneAtImageBorderCrossXSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtOriginCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneAtImageBorderCrossXSection && xzPlaneAtImageBorderCrossYSection && xyPlaneNotCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtImageBorderCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneAtOriginCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtOriginCrossZSection) || (yzPlaneNotCrossYSection && xzPlaneAtImageBorderCrossYSection && xyPlaneAtImageBorderCrossZSection)) || (yzPlaneAtImageBorderCrossXSection && xzPlaneNotCrossYSection && xyPlaneAtImageBorderCrossZSection) ) ) */ { // x-Axis if(xMin <= m_Origin[0] && xMax >= m_Origin[0]) { xMin = m_Origin[0]; }else if(xMin <= m_Size[0] * m_Spacing[0] && xMax >= m_Size[0] * m_Spacing[0]) { xMax = m_Size[0] * m_Spacing[0]; } // y-Axis if(yMin <= m_Origin[1] && yMax >= m_Origin[1]) { yMin = m_Origin[1]; }else if(yMin <= m_Size[1] * m_Spacing[1] && yMax >= m_Size[1] * m_Spacing[1]) { yMax = m_Size[1] * m_Spacing[1]; } // z-Axis if(zMin <= m_Origin[2] && zMax >= m_Origin[2]) { zMin = m_Origin[2]; }else if(zMin <= m_Size[2] * m_Spacing[2] && zMax >= m_Size[2] * m_Spacing[2]) { zMax = m_Size[2] * m_Spacing[2]; } m_meanValueTemp = m_meanValueTemp + this->MultiGaussianFunctionValueAtCuboid( xMin, xMax, yMin, yMax, zMin, zMax ); m_Volume = m_Volume + (xMax - xMin) * (yMax - yMin) * (zMax - zMin) ; } } } //----------------------------------------------------------------------------------------------------------------------- /* Start the octree recursion in eigth directions for the sphere with midpoint globalCoordinateMidpointSphere and, if uncommented, write this in a file, so that we can visualise it. */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::GenerateCuboidSegmentationInSphere(PointType globalCoordinateMidpointSphere) { double cuboidRadius = m_Radius * 0.5; PointType newMidpoint; for(int i = -1; i < 2; i+=2) { for(int k = -1; k < 2; k+=2) { for(int j = -1; j < 2; j+=2) { newMidpoint[0] = globalCoordinateMidpointSphere[0] + static_cast(i) * cuboidRadius; newMidpoint[1] = globalCoordinateMidpointSphere[1] + static_cast(k) * cuboidRadius; newMidpoint[2] = globalCoordinateMidpointSphere[2] + static_cast(j) * cuboidRadius; CalculateEdgesInSphere( newMidpoint, globalCoordinateMidpointSphere, cuboidRadius, 0); } } } if(m_WriteMPS) { m_WriteMPS = 0; // uncomment to write an .mps file to visualise the midpoints // std::cout << "Wrote .xml to visualise the midpoints." << std::endl; // WriteXMLToTestTheCuboidInsideTheSphere(); } } //---------------------------------------------------------------------------------------------------------------------- /* This class allows by the method CalculateTheMidpointAndTheMeanValueWithOctree() to find a sphere with a specified radius that has a maximal mean value over all sphere with that radius with midpoint inside or at the boundary of the image. We approximaze the sphere with the octree recursiv method. CalculateTheMidpointAndTheMeanValueWithOctree works as follows: 1. The for-loops traverse the region of interest and assume the current point to be the wanted sphere midpoint. 2. Calculate the mean value for that sphere. 3. Compare with the until-now-found-maximum and take the bigger one. */ template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::CalculateTheMidpointAndTheMeanValueWithOctree() { m_MeanValue = 0.0; double meanValueTemp; PointType midpoint; //typename MapContainerPoints::ElementIdentifier cuboidNumber = m_Midpoints.Size(); // SetNormalDistributionValues(); //double radius; //double xMin, xMax, yMin, yMax, zMin, zMax; m_WriteMPS = 1; PointType globalCoordinateMidpointSphere; IndexType index; OutputImageRegionType regionOfInterest; IndexType indexR; indexR.SetElement( 0, m_RegionOfInterestMin[0] ); indexR.SetElement( 1, m_RegionOfInterestMin[1] ); indexR.SetElement( 2, m_RegionOfInterestMin[2] ); regionOfInterest.SetIndex(indexR); SizeType sizeROI; sizeROI.SetElement( 0, m_RegionOfInterestMax[0] - m_RegionOfInterestMin[0] + 1); sizeROI.SetElement( 1, m_RegionOfInterestMax[1] - m_RegionOfInterestMin[1] + 1); sizeROI.SetElement( 2, m_RegionOfInterestMax[2] - m_RegionOfInterestMin[2] + 1); regionOfInterest.SetSize(sizeROI); typename TOutputImage::Pointer image = this->GetOutput(0); IteratorType regionOfInterestIterator(image, regionOfInterest); for(regionOfInterestIterator.GoToBegin(); !regionOfInterestIterator.IsAtEnd(); ++regionOfInterestIterator) { index = regionOfInterestIterator.GetIndex(); image->TransformIndexToPhysicalPoint(index, globalCoordinateMidpointSphere); m_Volume = 0.0; m_meanValueTemp = 0.0; this->GenerateCuboidSegmentationInSphere(globalCoordinateMidpointSphere); meanValueTemp = m_meanValueTemp / m_Volume; // std::cout << "index: " << index <<" meanvalue: " << meanValueTemp << std::endl; if(meanValueTemp > m_MeanValue) { m_GlobalCoordinate = globalCoordinateMidpointSphere; m_MeanValue = meanValueTemp; m_SphereMidpoint = index; } } } //---------------------------------------------------------------------------------------------------------------------- /* Check if a cuboid intersect the sphere boundary. Returns 2, if the cuboid is inside the sphere; returns 1, if the cuboid intersects the sphere boundary and 0, if the cuboid is out of the sphere. */ template< class TOutputImage > unsigned int MultiGaussianImageSource< TOutputImage > ::IntersectTheSphere( PointType globalCoordinateMidpointCuboid, PointType globalCoordinateMidpointSphere, double cuboidRadius) { unsigned int intersect = 1; PointType cuboidEdge; int count = 0; for(int i = -1; i < 2; i+=2) { for(int k = -1; k < 2; k+=2) { for(int j = -1; j < 2; j+=2) { cuboidEdge[0] = globalCoordinateMidpointCuboid[0] + static_cast(i) * cuboidRadius; cuboidEdge[1] = globalCoordinateMidpointCuboid[1] + static_cast(k) * cuboidRadius; cuboidEdge[2] = globalCoordinateMidpointCuboid[2] + static_cast(j) * cuboidRadius; if (globalCoordinateMidpointSphere.SquaredEuclideanDistanceTo(cuboidEdge) <= m_Radius * m_Radius) { ++count; } } } } if ( count == 0 ) { // cuboid not in the sphere intersect = 0; } if (count == 8 ) { // cuboid in the sphere intersect = 2; } return intersect; } //---------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > double MultiGaussianImageSource< TOutputImage > ::MultiGaussianFunctionValueAtPoint(double x, double y, double z) { //this claculate the mean value in the voxel //integrate over the voxel with midpoint [x, y, z] double summand0, summand1, summand2/*, power*/, value = 0.0, factor; double xMin, xMax, yMin, yMax, zMin, zMax, mean; mean = 0.0; // the for-loop represent the sum of the gaussian function xMin = x - m_Spacing[0] / 2.0; xMax = x + m_Spacing[0] / 2.0; yMin = y - m_Spacing[1] / 2.0; yMax = y + m_Spacing[1] / 2.0; zMin = z - m_Spacing[2] / 2.0; zMax = z + m_Spacing[2] / 2.0; for( unsigned int n = 0; n < m_NumberOfGaussians; ++n ) { summand0 = FunctionPhi( (xMax - m_CenterX[n]) / m_SigmaX[n] ) - FunctionPhi( (xMin - m_CenterX[n]) / m_SigmaX[n] ); summand1 = FunctionPhi( (yMax - m_CenterY[n]) / m_SigmaY[n] ) - FunctionPhi( (yMin - m_CenterY[n]) / m_SigmaY[n] ); summand2 = FunctionPhi( (zMax - m_CenterZ[n]) / m_SigmaZ[n] ) - FunctionPhi( (zMin - m_CenterZ[n]) / m_SigmaZ[n] ); value = summand0 * summand1 * summand2; factor = ( m_SigmaX[n] * m_SigmaY[n] * m_SigmaZ[n] ) * pow( 2.0 * itk::Math::pi, 1.5 ); mean = mean + factor * value * m_Altitude[n]; } value = mean / (m_Spacing[0] * m_Spacing[1] * m_Spacing[2] ); /* //this calculate the value of the gaussian at the midpoint of the voxel: double summand0, summand1, summand2, power, value = 0.0; // the for-loop represent the sum of the gaussian function for(unsigned int n =0; n < m_NumberOfGaussians; ++n) { summand0 = ( x - m_CenterX[n] ) / m_SigmaX[n]; summand1 = ( y - m_CenterY[n] ) / m_SigmaY[n]; summand2 = ( z - m_CenterZ[n] ) / m_SigmaZ[n]; power = summand0 * summand0 + summand1 * summand1 + summand2 * summand2; value = value + m_Altitude[n] * pow(itk::Math::e, -0.5 * power); } */ // std::cout << "X: " << xMin << " " << x << " "<< xMax << " Y: "<< yMin << " " << y << " " << yMax << " Z: "<< zMin << " "<< z << " " << zMax << " value: " << value << std::endl; return value; } //---------------------------------------------------------------------------------------------------------------------- template< class TOutputImage > void MultiGaussianImageSource< TOutputImage > ::AddGaussian( VectorType x, VectorType y, VectorType z, VectorType sx, VectorType sy, VectorType sz, VectorType altitude) { for(unsigned int i = 0; i < x.size(); ++i) { m_CenterX.push_back( x[i] ); m_CenterY.push_back( y[i] ); m_CenterZ.push_back( z[i] ); m_SigmaX.push_back( sx[i] ); m_SigmaY.push_back( sy[i] ); m_SigmaZ.push_back( sz[i] ); m_Altitude.push_back(altitude[i]); } } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::GenerateOutputInformation() { TOutputImage *output; IndexType index; index.Fill(0); output = this->GetOutput(0); typename TOutputImage::RegionType largestPossibleRegion; largestPossibleRegion.SetSize(this->m_Size); largestPossibleRegion.SetIndex(index); output->SetLargestPossibleRegion(largestPossibleRegion); output->SetSpacing(m_Spacing); output->SetOrigin(m_Origin); } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::GenerateData() { itkDebugMacro(<< "Generating a image of scalars "); double valueReal; IndexType index; - typedef typename TOutputImage::PixelType scalarType; typename TOutputImage::Pointer image = this->GetOutput(0); image = this->GetOutput(0); image->SetBufferedRegion( image->GetRequestedRegion() ); image->Allocate(); IteratorType imageIt(image, image->GetLargestPossibleRegion()); PointType globalCoordinate; this->SetNormalDistributionValues(); for(imageIt.GoToBegin(); !imageIt.IsAtEnd(); ++imageIt) { valueReal = 0.0; index = imageIt.GetIndex(); image->TransformIndexToPhysicalPoint(imageIt.GetIndex(), globalCoordinate); valueReal = MultiGaussianFunctionValueAtPoint(globalCoordinate[0], globalCoordinate[1] ,globalCoordinate[2]); imageIt.Set(valueReal); } } //----------------------------------------------------------------------------------------------------------------------- /*This method is used to write a .mps file, so that we can visualize the midpoints of the approximated sphere as a scatterplot (for example with MITK Workbench). */ template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::WriteXMLToTestTheCuboidInsideTheSphere() { std::stringstream ss; int numberSummand = 1.0; //write an .mps test file itk::DOMNodeXMLWriter::Pointer xmlWriter; typedef itk::DOMNode::Pointer DOMNodeType; DOMNodeType domXML, domPointSetFile, domFileVersion, domPointSet, domPoint, domId, domX, domY, domZ; xmlWriter = itk::DOMNodeXMLWriter::New(); domXML = itk::DOMNode::New(); domXML->SetName("?xml"); domPointSetFile = itk::DOMNode::New(); domPointSetFile->SetName("point_set_file"); //domFileVersion = itk::DOMNode::New(); //domFileVersion->SetName("file_version"); domPointSet = itk::DOMNode::New(); domPointSet->SetName("point_set"); ss.str(""); ss << 1.0; domXML->SetAttribute("version", ss.str()); domXML->AddChildAtBegin(domPointSetFile); //domPointSetFile -> AddChildAtBegin(domFileVersion); domPointSetFile -> AddChildAtBegin(domPointSet); unsigned int cap = m_Midpoints.Size(); for(unsigned int iter = 0 ; iter < cap; ++iter) { domPoint = itk::DOMNode::New(); domPoint->SetName("point"); domX = itk::DOMNode::New(); domX->SetName("x"); domY = itk::DOMNode::New(); domY->SetName("y"); domZ = itk::DOMNode::New(); domZ->SetName("z"); domId = itk::DOMNode::New(); domId->SetName("id"); ss.str(""); ss << numberSummand; domId->AddTextChildAtBegin(ss.str()); domPoint -> AddChildAtEnd(domId); double scaleFactor = 10.0; PointType point = m_Midpoints.GetElement( numberSummand - 1 ); ss.str(""); ss << point[0] * scaleFactor; domX->AddTextChildAtBegin(ss.str()); domPoint -> AddChildAtEnd(domX); ss.str(""); ss << point[1] * scaleFactor; domY->AddTextChildAtBegin(ss.str()); domPoint -> AddChildAtEnd(domY); ss.str(""); ss << point[2] * scaleFactor; domZ->AddTextChildAtBegin(ss.str()); domPoint -> AddChildAtEnd(domZ); domPointSet -> AddChildAtEnd(domPoint); numberSummand += 1.0; } // .mps (Data) ss.str(""); ss << "C:/temp/CuboidsInTheSphere.mps"; std::string name = ss.str(); char * fileNamePointer = (char*) name.c_str(); xmlWriter->SetFileName( fileNamePointer); xmlWriter->SetInput( domXML ); xmlWriter->Update(); } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::CalculateMaxAndMinInSphere() { IndexType index; typename MultiGaussianImageSource< TOutputImage >::OutputImagePixelType value; m_MaxValueInSphere = std::numeric_limits::min(); m_MinValueInSphere = std::numeric_limits::max(); int radInt, sizeRegion; OutputImageRegionType cuboidRegion; IndexType indexR; SizeType sizeR; int indexRegion, originAsIndex; for( unsigned int i = 0; i < 3; ++i ) { radInt = static_cast(m_Radius/m_Spacing[i]); indexRegion = m_SphereMidpoint[i] - radInt; originAsIndex = static_cast(m_Origin[i]/m_Spacing[i]); if( originAsIndex > indexRegion ) { indexR.SetElement(i, originAsIndex ); } else { indexR.SetElement(i, indexRegion ); } sizeRegion = 2 *radInt + 1; int sizeOutputImage = m_Size[i]; if( (indexR[i] + sizeRegion) > (originAsIndex + sizeOutputImage) ) { std::cout << "Not the entire sphere is in the image!" << std::endl; sizeR.SetElement(i, m_Size[i] - indexRegion ); } else { sizeR.SetElement(i, sizeRegion ); } } cuboidRegion.SetIndex(indexR); cuboidRegion.SetSize(sizeR); typename TOutputImage::Pointer image = this->GetOutput(0); IteratorType cuboidRegionOfInterestIterator(image, cuboidRegion); PointType globalCoordinate; for(cuboidRegionOfInterestIterator.GoToBegin(); !cuboidRegionOfInterestIterator.IsAtEnd(); ++cuboidRegionOfInterestIterator) { index = cuboidRegionOfInterestIterator.GetIndex(); if(IsInImage(index)) { image->TransformIndexToPhysicalPoint(cuboidRegionOfInterestIterator.GetIndex(), globalCoordinate); if( m_GlobalCoordinate.EuclideanDistanceTo(globalCoordinate) <= m_Radius ) { value = cuboidRegionOfInterestIterator.Get(); if(m_MaxValueInSphere < value) { m_MaxValueInSphere = value; m_MaxValueIndexInSphere = index; } if(m_MinValueInSphere > value) { m_MinValueInSphere = value; m_MinValueIndexInSphere = index; } } } } } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > bool MultiGaussianImageSource< TOutputImage > ::IsInImage(IndexType index) { bool isInImage = true; int originAsIndex; for( unsigned int i = 0; i < 3; ++i ) { originAsIndex = static_cast(m_Origin[i]/m_Spacing[i]); int sizeOfOutputImage = m_Size[i]; if( index[i] < originAsIndex || index[i] > (originAsIndex + sizeOfOutputImage) ) return false; } return isInImage; } //----------------------------------------------------------------------------------------------------------------------- template< typename TOutputImage > void MultiGaussianImageSource< TOutputImage > ::SetNormalDistributionValues() { double temp[410] = { 0.5 , 0.50399 , 0.50798, 0.51197, 0.51595, 0.51994, 0.52392, 0.5279, 0.53188, 0.53586, 0.53983, 0.5438, 0.54776, 0.55172, 0.55567, 0.55962, 0.56356, 0.56749, 0.57142, 0.57535, 0.57926, 0.58317, 0.58706, 0.59095, 0.59483 , 0.59871, 0.60257, 0.60642, 0.61026, 0.61409, 0.61791, 0.62172, 0.62552, 0.6293, 0.63307, 0.63683, 0.64058, 0.64431, 0.64803, 0.65173, 0.65542, 0.6591, 0.66276, 0.6664, 0.67003, 0.67364, 0.67724, 0.68082, 0.68439, 0.68793, 0.69146, 0.69497, 0.69847, 0.70194, 0.7054, 0.70884, 0.71226, 0.71566, 0.71904, 0.7224, 0.72575, 0.72907, 0.73237, 0.73565, 0.73891, 0.74215, 0.74537, 0.74857, 0.75175, 0.7549, 0.75804, 0.76115, 0.76424, 0.7673, 0.77035, 0.77337, 0.77637, 0.77935, 0.7823, 0.78524, 0.78814, 0.79103, 0.79389, 0.79673, 0.79955, 0.80234, 0.80511, 0.80785, 0.81057, 0.81327, 0.81594, 0.81859, 0.82121, 0.82381, 0.82639, 0.82894, 0.83147, 0.83398, 0.83646, 0.83891, 0.84134, 0.84375, 0.84614, 0.84849, 0.85083, 0.85314, 0.85543, 0.85769, 0.85993, 0.86214, 0.86433, 0.8665, 0.86864, 0.87076, 0.87286, 0.87493, 0.87698, 0.879, 0.881, 0.88298, 0.88493, 0.88686, 0.88877, 0.89065, 0.89251, 0.89435, 0.89617, 0.89796, 0.89973, 0.90147, 0.9032, 0.9049, 0.90658, 0.90824, 0.90988, 0.91149, 0.91309, 0.91466, 0.91621, 0.91774, 0.91924, 0.92073, 0.9222, 0.92364, 0.92507, 0.92647, 0.92785, 0.92922, 0.93056, 0.93189, 0.93319, 0.93448, 0.93574, 0.93699, 0.93822, 0.93943, 0.94062, 0.94179, 0.94295, 0.94408, 0.9452, 0.9463, 0.94738, 0.94845, 0.9495, 0.95053, 0.95154, 0.95254, 0.95352, 0.95449, 0.95543, 0.95637, 0.95728, 0.95818, 0.95907, 0.95994, 0.9608, 0.96164, 0.96246, 0.96327, 0.96407, 0.96485, 0.96562, 0.96638, 0.96712, 0.96784, 0.96856, 0.96926, 0.96995, 0.97062, 0.97128, 0.97193, 0.97257, 0.9732, 0.97381, 0.97441, 0.975, 0.97558, 0.97615, 0.9767, 0.97725, 0.97778, 0.97831, 0.97882, 0.97932, 0.97982, 0.9803, 0.98077, 0.98124, 0.98169, 0.98214, 0.98257, 0.983, 0.98341, 0.98382, 0.98422, 0.98461, 0.985, 0.98537, 0.98574, 0.9861, 0.98645, 0.98679, 0.98713, 0.98745, 0.98778, 0.98809, 0.9884, 0.9887, 0.98899, 0.98928, 0.98956, 0.98983, 0.9901, 0.99036, 0.99061, 0.99086, 0.99111, 0.99134, 0.99158, 0.9918, 0.99202, 0.99224, 0.99245, 0.99266, 0.99286, 0.99305, 0.99324, 0.99343, 0.99361, 0.99379, 0.99396, 0.99413, 0.9943, 0.99446, 0.99461, 0.99477, 0.99492, 0.99506, 0.9952, 0.99534, 0.99547, 0.9956, 0.99573, 0.99585, 0.99598, 0.99609, 0.99621, 0.99632, 0.99643, 0.99653, 0.99664, 0.99674, 0.99683, 0.99693, 0.99702, 0.99711, 0.9972, 0.99728, 0.99736, 0.99744, 0.99752, 0.9976, 0.99767, 0.99774, 0.99781, 0.99788, 0.99795, 0.99801, 0.99807, 0.99813, 0.99819, 0.99825, 0.99831, 0.99836, 0.99841, 0.99846, 0.99851, 0.99856, 0.99861, 0.99865, 0.99869, 0.99874, 0.99878, 0.99882, 0.99886, 0.99889, 0.99893, 0.99896, 0.999, 0.99903, 0.99906, 0.9991, 0.99913, 0.99916, 0.99918, 0.99921, 0.99924, 0.99926, 0.99929, 0.99931, 0.99934, 0.99936, 0.99938, 0.9994, 0.99942, 0.99944, 0.99946, 0.99948, 0.9995, 0.99952, 0.99953, 0.99955, 0.99957, 0.99958, 0.9996, 0.99961, 0.99962, 0.99964, 0.99965, 0.99966, 0.99968, 0.99969, 0.9997, 0.99971, 0.99972, 0.99973, 0.99974, 0.99975, 0.99976, 0.99977, 0.99978, 0.99978, 0.99979, 0.9998, 0.99981, 0.99981, 0.99982, 0.99983, 0.99983, 0.99984, 0.99985, 0.99985, 0.99986, 0.99986, 0.99987, 0.99987, 0.99988, 0.99988, 0.99989, 0.99989, 0.9999, 0.9999, 0.9999, 0.99991, 0.99991, 0.99992, 0.99992, 0.99992, 0.99992, 0.99993, 0.99993, 0.99993, 0.99994, 0.99994, 0.99994, 0.99994, 0.99995, 0.99995, 0.99995, 0.99995, 0.99995, 0.99996, 0.99996, 0.99996, 0.99996, 0.99996, 0.99996, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99997, 0.99998, 0.99998, 0.99998, 0.99998 }; for(int i=0; i < 410; i++) { m_NormalDistValues[i] = temp[i]; } } } // end namespace itk #endif diff --git a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx index 57e4dda221..403574bbda 100644 --- a/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx +++ b/Modules/ImageStatistics/mitkExtendedLabelStatisticsImageFilter.hxx @@ -1,765 +1,760 @@ /*=================================================================== 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 _mitkExtendedLabelStatisticsImageFilter_hxx #define _mitkExtendedLabelStatisticsImageFilter_hxx #include "mitkExtendedLabelStatisticsImageFilter.h" #include "itkImageRegionConstIteratorWithIndex.h" #include "itkImageRegionConstIterator.h" #include #include #include "mitkNumericConstants.h" #include "mitkLogMacros.h" #include namespace itk { template< class TInputImage, class TLabelImage > bool ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetMaskingNonEmpty() const { return m_MaskNonEmpty; } template< typename TInputImage, typename TLabelImage > void ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::SetHistogramParameters(const int numBins, RealType lowerBound, RealType upperBound) { m_NumBins[0] = numBins; m_LowerBound = lowerBound; m_UpperBound = upperBound; m_GlobalHistogramParametersSet = true; m_PreferGlobalHistogramParameters = true; this->Modified(); } template< typename TInputImage, typename TLabelImage > void ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::SetHistogramParametersForLabels(std::map numBins, std::map lowerBound, std::map upperBound) { m_LabelMin = lowerBound; m_LabelMax = upperBound; m_LabelNBins = numBins; m_LabelHistogramParametersSet = true; m_PreferGlobalHistogramParameters = false; this->Modified(); } template< class TInputImage, class TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetUniformity(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::Zero; } else { return ( *mapIt ).second.m_Uniformity; } } template< class TInputImage, class TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetMedian(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::Zero; } else { return ( *mapIt ).second.m_Median; } } template< class TInputImage, class TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetEntropy(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::Zero; } else { return ( *mapIt ).second.m_Entropy; } } template< class TInputImage, class TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetUPP(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::Zero; } else { return ( *mapIt ).second.m_UPP; } } template< class TInputImage, class TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetMPP(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::Zero; } else { return ( *mapIt ).second.m_MPP; } } template< class TInputImage, class TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetKurtosis(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::Zero; } else { return ( *mapIt ).second.m_Kurtosis; } } template< class TInputImage, class TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetSkewness(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::Zero; } else { return ( *mapIt ).second.m_Skewness; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetMinimum(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::max(); } else { return ( *mapIt ).second.m_Minimum; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetMaximum(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::NonpositiveMin(); } else { return ( *mapIt ).second.m_Maximum; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetMean(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::ZeroValue(); } else { return ( *mapIt ).second.m_Mean; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetSum(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::ZeroValue(); } else { return ( *mapIt ).second.m_Sum; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetSigma(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::ZeroValue(); } else { return ( *mapIt ).second.m_Sigma; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RealType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetVariance(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return NumericTraits< PixelType >::ZeroValue(); } else { return ( *mapIt ).second.m_Variance; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::BoundingBoxType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetBoundingBox(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { typename Superclass::BoundingBoxType emptyBox; // label does not exist, return a default value return emptyBox; } else { return ( *mapIt ).second.m_BoundingBox; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::RegionType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetRegion(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { typename Superclass::RegionType emptyRegion; // label does not exist, return a default value return emptyRegion; } else { typename Superclass::BoundingBoxType bbox = this->GetBoundingBox(label); typename Superclass::IndexType index; typename Superclass::SizeType size; unsigned int dimension = bbox.size() / 2; for ( unsigned int i = 0; i < dimension; i++ ) { index[i] = bbox[2 * i]; size[i] = bbox[2 * i + 1] - bbox[2 * i] + 1; } typename Superclass::RegionType region; region.SetSize(size); region.SetIndex(index); return region; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::MapSizeType ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetCount(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return 0; } else { return ( *mapIt ).second.m_Count; } } template< typename TInputImage, typename TLabelImage > typename ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >::HistogramType::Pointer ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetHistogram(LabelPixelType label) const { StatisticsMapConstIterator mapIt; mapIt = m_LabelStatistics.find(label); if ( mapIt == m_LabelStatistics.end() ) { // label does not exist, return a default value return ITK_NULLPTR; } else { // this will be zero if histograms have not been enabled return ( *mapIt ).second.m_Histogram; } } template< typename TInputImage, typename TLabelImage > void ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::BeforeThreadedGenerateData() { ThreadIdType numberOfThreads = this->GetNumberOfThreads(); // Resize the thread temporaries m_LabelStatisticsPerThread.resize(numberOfThreads); // Initialize the temporaries for ( ThreadIdType i = 0; i < numberOfThreads; ++i ) { m_LabelStatisticsPerThread[i].clear(); } // Initialize the final map m_LabelStatistics.clear(); } template< typename TInputImage, typename TLabelImage > std::list ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::GetRelevantLabels() const { std::list< int> relevantLabels; for (int i = 0; i < 4096; ++i ) { if ( this->HasLabel( i ) ) { relevantLabels.push_back( i ); } } return relevantLabels; } template< typename TInputImage, typename TLabelImage > void ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage > ::ThreadedGenerateData(const typename TInputImage::RegionType & outputRegionForThread, ThreadIdType threadId) { typename HistogramType::IndexType histogramIndex(1); typename HistogramType::MeasurementVectorType histogramMeasurement(1); const SizeValueType size0 = outputRegionForThread.GetSize(0); if( size0 == 0) { return; } ImageLinearConstIteratorWithIndex< TInputImage > it (this->GetInput(), outputRegionForThread); ImageScanlineConstIterator< TLabelImage > labelIt (this->GetLabelInput(), outputRegionForThread); StatisticsMapIterator mapIt; // support progress methods/callbacks const size_t numberOfLinesToProcess = outputRegionForThread.GetNumberOfPixels() / size0; ProgressReporter progress( this, threadId, numberOfLinesToProcess ); typedef typename MapType::value_type MapValueType; // do the work while ( !it.IsAtEnd() ) { while ( !it.IsAtEndOfLine() ) { const RealType & value = static_cast< RealType >( it.Get() ); const LabelPixelType & label = labelIt.Get(); // is the label already in this thread? mapIt = m_LabelStatisticsPerThread[threadId].find(label); if ( mapIt == m_LabelStatisticsPerThread[threadId].end() ) { // if global histogram parameters are set and preferred then use them if ( m_PreferGlobalHistogramParameters && m_GlobalHistogramParametersSet ) { mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, LabelStatistics(m_NumBins[0], m_LowerBound, m_UpperBound) ) ).first; } // if we have label histogram parameters then use them. If we encounter a label that has no parameters then use global settings if available else if(!m_PreferGlobalHistogramParameters && m_LabelHistogramParametersSet) { typename std::map::iterator lbIt, ubIt; typename std::map::iterator nbIt; lbIt = m_LabelMin.find(label); ubIt = m_LabelMax.find(label); nbIt = m_LabelNBins.find(label); // if any of the parameters is lacking for the current label but global histogram params are available, use the global parameters if ((lbIt == m_LabelMin.end() || ubIt == m_LabelMax.end() || nbIt == m_LabelNBins.end()) && m_GlobalHistogramParametersSet) { mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, LabelStatistics(m_NumBins[0], m_LowerBound, m_UpperBound) ) ).first; } // if any of the parameters is lacking for the current label and global histogram params are not available, dont use histograms for this label else if ((lbIt == m_LabelMin.end() || ubIt == m_LabelMax.end() || nbIt == m_LabelNBins.end()) && !m_GlobalHistogramParametersSet) { mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, LabelStatistics() ) ).first; } // label histogram parameters are available, use them! else { PixelType lowerBound, upperBound; unsigned int nBins; lowerBound = (*lbIt).second; upperBound = (*ubIt).second; nBins = (*nbIt).second; mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, LabelStatistics(nBins, lowerBound, upperBound) ) ).first; } } // neither global nor label specific histogram parameters are set -> don't use histograms else { mapIt = m_LabelStatisticsPerThread[threadId].insert( MapValueType( label, LabelStatistics() ) ).first; } } typename MapType::mapped_type &labelStats = ( *mapIt ).second; // update the values for this label and this thread if ( value < labelStats.m_Minimum ) { labelStats.m_Minimum = value; } if ( value > labelStats.m_Maximum ) { labelStats.m_Maximum = value; } // bounding box is min,max pairs for ( unsigned int i = 0; i < ( 2 * TInputImage::ImageDimension ); i += 2 ) { const typename TInputImage::IndexType & index = it.GetIndex(); if ( labelStats.m_BoundingBox[i] > index[i / 2] ) { labelStats.m_BoundingBox[i] = index[i / 2]; } if ( labelStats.m_BoundingBox[i + 1] < index[i / 2] ) { labelStats.m_BoundingBox[i + 1] = index[i / 2]; } } labelStats.m_Sum += value; labelStats.m_SumOfSquares += ( value * value ); labelStats.m_Count++; labelStats.m_SumOfCubes += std::pow(value, 3.); labelStats.m_SumOfQuadruples += std::pow(value, 4.); if (value > 0) { labelStats.m_PositivePixelCount++; labelStats.m_SumOfPositivePixels += value; } // if enabled, update the histogram for this label if ( labelStats.m_Histogram.IsNotNull() ) - { + { histogramMeasurement[0] = value; labelStats.m_Histogram->GetIndex(histogramMeasurement, histogramIndex); labelStats.m_Histogram->IncreaseFrequencyOfIndex(histogramIndex, 1); - } - else - { - int x = 0; } - ++labelIt; ++it; } labelIt.NextLine(); it.NextLine(); progress.CompletedPixel(); } } template< class TInputImage, class TLabelImage > void ExtendedLabelStatisticsImageFilter< TInputImage, TLabelImage >:: AfterThreadedGenerateData() { StatisticsMapIterator mapIt; StatisticsMapConstIterator threadIt; ThreadIdType i; ThreadIdType numberOfThreads = this->GetNumberOfThreads(); // Run through the map for each thread and accumulate the count, // sum, and sumofsquares for ( i = 0; i < numberOfThreads; i++ ) { // iterate over the map for this thread for ( threadIt = m_LabelStatisticsPerThread[i].begin(); threadIt != m_LabelStatisticsPerThread[i].end(); ++threadIt ) { // does this label exist in the cumulative structure yet? mapIt = m_LabelStatistics.find( ( *threadIt ).first ); if ( mapIt == m_LabelStatistics.end() ) { // create a new entry typedef typename MapType::value_type MapValueType; if ( m_GlobalHistogramParametersSet || m_LabelHistogramParametersSet ) { // mapIt = m_LabelStatistics.insert( MapValueType( ( *threadIt ).first, // LabelStatistics(m_NumBins[0], m_LowerBound, // m_UpperBound) ) ).first; mapIt = m_LabelStatistics.insert( MapValueType( *threadIt ) ).first; continue; } else { mapIt = m_LabelStatistics.insert( MapValueType( ( *threadIt ).first, LabelStatistics() ) ).first; } } typename MapType::mapped_type &labelStats = ( *mapIt ).second; // accumulate the information from this thread labelStats.m_Count += ( *threadIt ).second.m_Count; labelStats.m_Sum += ( *threadIt ).second.m_Sum; labelStats.m_SumOfSquares += ( *threadIt ).second.m_SumOfSquares; labelStats.m_SumOfPositivePixels += ( *threadIt ).second.m_SumOfPositivePixels; labelStats.m_PositivePixelCount += ( *threadIt ).second.m_PositivePixelCount; labelStats.m_SumOfCubes += ( *threadIt ).second.m_SumOfCubes; labelStats.m_SumOfQuadruples += ( *threadIt ).second.m_SumOfQuadruples; if ( labelStats.m_Minimum > ( *threadIt ).second.m_Minimum ) { labelStats.m_Minimum = ( *threadIt ).second.m_Minimum; } if ( labelStats.m_Maximum < ( *threadIt ).second.m_Maximum ) { labelStats.m_Maximum = ( *threadIt ).second.m_Maximum; } //bounding box is min,max pairs int dimension = labelStats.m_BoundingBox.size() / 2; for ( int ii = 0; ii < ( dimension * 2 ); ii += 2 ) { if ( labelStats.m_BoundingBox[ii] > ( *threadIt ).second.m_BoundingBox[ii] ) { labelStats.m_BoundingBox[ii] = ( *threadIt ).second.m_BoundingBox[ii]; } if ( labelStats.m_BoundingBox[ii + 1] < ( *threadIt ).second.m_BoundingBox[ii + 1] ) { labelStats.m_BoundingBox[ii + 1] = ( *threadIt ).second.m_BoundingBox[ii + 1]; } } // if enabled, update the histogram for this label if ( m_GlobalHistogramParametersSet || m_LabelHistogramParametersSet ) { typename HistogramType::IndexType index; index.SetSize(1); for ( unsigned int bin = 0; bin < labelStats.m_Histogram->Size(); bin++ ) { index[0] = bin; labelStats.m_Histogram->IncreaseFrequency( bin, ( *threadIt ).second.m_Histogram->GetFrequency(bin) ); } } } // end of thread map iterator loop } // end of thread loop // compute the remainder of the statistics for ( mapIt = m_LabelStatistics.begin(); mapIt != m_LabelStatistics.end(); ++mapIt ) { typename MapType::mapped_type &labelStats = ( *mapIt ).second; // mean labelStats.m_Mean = labelStats.m_Sum / static_cast< RealType >( labelStats.m_Count ); // MPP labelStats.m_MPP = labelStats.m_SumOfPositivePixels / static_cast< RealType >( labelStats.m_PositivePixelCount ); // variance if ( labelStats.m_Count > 0 ) { // unbiased estimate of variance LabelStatistics & ls = mapIt->second; const RealType sumSquared = ls.m_Sum * ls.m_Sum; const RealType count = static_cast< RealType >( ls.m_Count ); ls.m_Variance = ( ls.m_SumOfSquares - sumSquared / count ) / ( count ); RealType secondMoment = ls.m_SumOfSquares / count; RealType thirdMoment = ls.m_SumOfCubes / count; RealType fourthMoment = ls.m_SumOfQuadruples / count; ls.m_Skewness = (thirdMoment - 3. * secondMoment * ls.m_Mean + 2. * std::pow(ls.m_Mean, 3.)) / std::pow(secondMoment - std::pow(ls.m_Mean, 2.), 1.5); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/skewness_impl.html ls.m_Kurtosis = (fourthMoment - 4. * thirdMoment * ls.m_Mean + 6. * secondMoment * std::pow(ls.m_Mean, 2.) - 3. * std::pow(ls.m_Mean, 4.)) / std::pow(secondMoment - std::pow(ls.m_Mean, 2.), 2.); // see http://www.boost.org/doc/libs/1_51_0/doc/html/boost/accumulators/impl/kurtosis_impl.html, dropped -3 } else { labelStats.m_Variance = NumericTraits< RealType >::ZeroValue(); labelStats.m_Skewness = NumericTraits< RealType >::ZeroValue(); labelStats.m_Kurtosis = NumericTraits< RealType >::ZeroValue(); } // sigma labelStats.m_Sigma = std::sqrt( labelStats.m_Variance ); // histogram statistics if (labelStats.m_Histogram.IsNotNull()) { mitk::HistogramStatisticsCalculator histStatCalc; histStatCalc.SetHistogram(labelStats.m_Histogram); histStatCalc.CalculateStatistics(); labelStats.m_Median = histStatCalc.GetMedian(); labelStats.m_Entropy = histStatCalc.GetEntropy(); labelStats.m_Uniformity = histStatCalc.GetUniformity(); labelStats.m_UPP = histStatCalc.GetUPP(); } } { //Now update the cached vector of valid labels. m_ValidLabelValues.resize(0); m_ValidLabelValues.reserve(m_LabelStatistics.size()); for ( mapIt = m_LabelStatistics.begin(); mapIt != m_LabelStatistics.end(); ++mapIt ) { m_ValidLabelValues.push_back(mapIt->first); } } } } // end namespace itk #endif diff --git a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h index fc4ea75878..75977cf508 100644 --- a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h +++ b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.h @@ -1,210 +1,211 @@ /*=================================================================== 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 #define __mitkExtendedStatisticsImageFilter #include "itkStatisticsImageFilter.h" #include #include namespace itk { /** * \class ExtendedStatisticsImageFilter * \brief Extension of the itkStatisticsImageFilter that also calculates the Skewness and Kurtosis. * * This class inherits from the itkStatisticsImageFilter and * uses its results for the calculation of other additional coefficients: * the Skewness and Kurtosis. * * As the StatisticsImageFilter stores the statistics in the outputs 1 to 6 by the * StatisticsImageFilter, the skewness, kurtosis, MPP, UPP, Uniformity, Entropy and Median are stored in the outputs * 7 to 14 by this filter. */ template< class TInputImage > class ExtendedStatisticsImageFilter : public StatisticsImageFilter< TInputImage > { public: /** Standard Self typedef */ typedef ExtendedStatisticsImageFilter Self; typedef StatisticsImageFilter< TInputImage > Superclass; typedef SmartPointer< Self > Pointer; typedef SmartPointer< const Self > ConstPointer; typedef typename Superclass::RealType RealType; typedef typename Superclass::RealObjectType RealObjectType; typedef typename Superclass::PixelType PixelType; /** Histogram-related typedefs */ typedef itk::Statistics::Histogram< RealType > HistogramType; typedef typename HistogramType::Pointer HistogramPointer; itkFactorylessNewMacro( Self ); itkCloneMacro( Self ); itkTypeMacro( ExtendedStatisticsImageFilter, StatisticsImageFilter ); /** * \brief Return the computed Skewness. */ double GetSkewness() const { return this->GetSkewnessOutput()->Get(); } /** * \brief Return the computed Median */ double GetMedian() const { return this->GetMedianOutput()->Get(); } /** * \brief Return the computed Kurtosis. */ double GetKurtosis() const { return this->GetKurtosisOutput()->Get(); } /* \brief Return the computed MPP. */ double GetMPP() const { return this->GetMPPOutput()->Get(); } /** * \brief Return the computed Uniformity. */ double GetUniformity() const { return this->GetUniformityOutput()->Get(); } /** *\brief Return the computed Entropy. */ double GetEntropy() const { return this->GetEntropyOutput()->Get(); } /** * \brief Return the computed UPP. */ double GetUPP() const { return this->GetUPPOutput()->Get(); } /** * \brief Return the computed Histogram. */ const typename HistogramType::Pointer GetHistogram() { if (m_HistogramCalculated) { return m_Histogram; } else { return nullptr; } } /** specify Histogram parameters */ void SetHistogramParameters(const int numBins, RealType lowerBound, RealType upperBound); protected: ExtendedStatisticsImageFilter(); virtual ~ExtendedStatisticsImageFilter(){}; void BeforeThreadedGenerateData(); /** Multi-thread version GenerateData. */ void ThreadedGenerateData(const typename StatisticsImageFilter::RegionType & outputRegionForThread, ThreadIdType threadId); /** * brief Calls AfterThreadedGenerateData() of the superclass and the main methods */ void AfterThreadedGenerateData(); RealObjectType* GetSkewnessOutput(); const RealObjectType* GetSkewnessOutput() const; RealObjectType* GetKurtosisOutput(); const RealObjectType* GetKurtosisOutput() const; RealObjectType* GetMPPOutput(); const RealObjectType* GetMPPOutput() const; RealObjectType* GetEntropyOutput(); const RealObjectType* GetEntropyOutput() const; RealObjectType* GetUniformityOutput(); const RealObjectType* GetUniformityOutput() const; RealObjectType* GetUPPOutput(); const RealObjectType* GetUPPOutput() const; RealObjectType* GetMedianOutput(); const RealObjectType* GetMedianOutput() const; - virtual DataObject::Pointer MakeOutput( ProcessObject::DataObjectPointerArraySizeType idx ); + using Superclass::MakeOutput; + virtual DataObject::Pointer MakeOutput( ProcessObject::DataObjectPointerArraySizeType idx ) override; private: Array< RealType > m_ThreadSum; Array< RealType > m_SumOfSquares; Array< RealType > m_SumOfCubes; Array< RealType > m_SumOfQuadruples; Array< SizeValueType > m_Count; Array< SizeValueType > m_PositivePixelCount; Array< RealType > m_ThreadSumOfPositivePixels; Array< PixelType > m_ThreadMin; Array< PixelType > m_ThreadMax; std::vector< HistogramPointer > m_HistogramPerThread; HistogramPointer m_Histogram; bool m_UseHistogram; bool m_HistogramCalculated; RealType m_LowerBound, m_UpperBound; int m_NumBins; }; // end of class } // end namespace itk #ifndef ITK_MANUAL_INSTANTIATION #include "mitkExtendedStatisticsImageFilter.hxx" #endif #endif diff --git a/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx b/Modules/ImageStatistics/mitkExtendedStatisticsImageFilter.hxx index 9413e33c0c..9ea49c069b 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 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_ThreadMin(1), + m_ThreadMax(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::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 ( 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/ImageStatistics/mitkHotspotMaskGenerator.cpp b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp index c1b74388eb..5f8a2f6844 100644 --- a/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp +++ b/Modules/ImageStatistics/mitkHotspotMaskGenerator.cpp @@ -1,603 +1,602 @@ #include #include #include #include #include #include "mitkImageAccessByItk.h" #include #include #include namespace mitk { HotspotMaskGenerator::HotspotMaskGenerator(): m_HotspotRadiusinMM(6.2035049089940), // radius of a 1cm3 sphere in mm m_HotspotMustBeCompletelyInsideImage(true), m_Label(1) { m_TimeStep = 0; m_InternalMask = mitk::Image::New(); m_InternalMaskUpdateTime = 0; } void HotspotMaskGenerator::SetInputImage(mitk::Image::Pointer inputImage) { if (inputImage != m_inputImage) { m_inputImage = inputImage; m_ConvolutionImageMaxIndex.set_size(inputImage->GetDimension()); m_ConvolutionImageMinIndex.set_size(inputImage->GetDimension()); this->Modified(); } } void HotspotMaskGenerator::SetMask(MaskGenerator::Pointer mask) { if (mask != m_Mask) { m_Mask = mask; this->Modified(); } } HotspotMaskGenerator::~HotspotMaskGenerator() { } void HotspotMaskGenerator::SetHotspotRadiusInMM(double radiusInMillimeter) { if(radiusInMillimeter != m_HotspotRadiusinMM) { m_HotspotRadiusinMM = radiusInMillimeter; this->Modified(); } } const double& HotspotMaskGenerator::GetHotspotRadiusinMM() const { return m_HotspotRadiusinMM; } bool HotspotMaskGenerator::GetHotspotMustBeCompletelyInsideImage() const { return m_HotspotMustBeCompletelyInsideImage; } void HotspotMaskGenerator::SetHotspotMustBeCompletelyInsideImage(bool mustBeCompletelyInImage) { if (m_HotspotMustBeCompletelyInsideImage != mustBeCompletelyInImage) { m_HotspotMustBeCompletelyInsideImage = mustBeCompletelyInImage; this->Modified(); } } mitk::Image::Pointer HotspotMaskGenerator::GetMask() { if (IsUpdateRequired()) { if ( m_inputImage.IsNull() ) { throw std::runtime_error( "Error: image empty!" ); } if ( m_TimeStep >= m_inputImage->GetTimeSteps() ) { throw std::runtime_error( "Error: invalid time step!" ); } mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput( m_inputImage ); imageTimeSelector->SetTimeNr( m_TimeStep ); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer timeSliceImage = imageTimeSelector->GetOutput(); m_internalImage = timeSliceImage; m_internalMask2D = nullptr; // is this correct when this variable holds a smart pointer? m_internalMask3D = nullptr; if ( m_Mask != nullptr ) { m_Mask->SetTimeStep(m_TimeStep); mitk::Image::Pointer timeSliceMask = m_Mask->GetMask(); if ( m_internalImage->GetDimension() == 3 ) { CastToItkImage(timeSliceMask, m_internalMask3D); AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 3, m_internalMask3D, m_Label); } else if ( m_internalImage->GetDimension() == 2 ) { CastToItkImage(timeSliceMask, m_internalMask2D); AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 2, m_internalMask2D, m_Label); } else { throw std::runtime_error( "Error: invalid image dimension" ); } } else { if ( m_internalImage->GetDimension() == 3 ) { AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 3, m_internalMask3D, m_Label); } else if ( m_internalImage->GetDimension() == 2 ) { AccessFixedDimensionByItk_2(m_internalImage, CalculateHotspotMask, 2, m_internalMask2D, m_Label); } else { throw std::runtime_error( "Error: invalid image dimension" ); } } this->Modified(); } m_InternalMaskUpdateTime = m_InternalMask->GetMTime(); return m_InternalMask; } void HotspotMaskGenerator::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; } } void HotspotMaskGenerator::SetLabel(unsigned short label) { if (label != m_Label) { m_Label = label; this->Modified(); } } vnl_vector HotspotMaskGenerator::GetConvolutionImageMinIndex() { this->GetMask(); // make sure we are up to date return m_ConvolutionImageMinIndex; } vnl_vector HotspotMaskGenerator::GetHotspotIndex() { this->GetMask(); // make sure we are up to date return m_ConvolutionImageMaxIndex; } template HotspotMaskGenerator::ImageExtrema HotspotMaskGenerator::CalculateExtremaWorld( const itk::Image* inputImage, typename itk::Image::Pointer maskImage, double neccessaryDistanceToImageBorderInMM, unsigned int label ) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typedef itk::ImageRegionConstIteratorWithIndex MaskImageIteratorType; typedef itk::ImageRegionConstIteratorWithIndex InputImageIndexIteratorType; typename ImageType::SpacingType spacing = inputImage->GetSpacing(); ImageExtrema minMax; minMax.Defined = false; minMax.MaxIndex.set_size(VImageDimension); minMax.MaxIndex.set_size(VImageDimension); typename ImageType::RegionType allowedExtremaRegion = inputImage->GetLargestPossibleRegion(); bool keepDistanceToImageBorders( neccessaryDistanceToImageBorderInMM > 0 ); if (keepDistanceToImageBorders) { itk::IndexValueType distanceInPixels[VImageDimension]; for(unsigned short dimension = 0; dimension < VImageDimension; ++dimension) { // To confirm that the whole hotspot is inside the image we have to keep a specific distance to the image-borders, which is as long as // the radius. To get the amount of indices we divide the radius by spacing and add 0.5 because voxels are center based: // For example with a radius of 2.2 and a spacing of 1 two indices are enough because 2.2 / 1 + 0.5 = 2.7 => 2. // But with a radius of 2.7 we need 3 indices because 2.7 / 1 + 0.5 = 3.2 => 3 distanceInPixels[dimension] = int( neccessaryDistanceToImageBorderInMM / spacing[dimension] + 0.5); } allowedExtremaRegion.ShrinkByRadius(distanceInPixels); } InputImageIndexIteratorType imageIndexIt(inputImage, allowedExtremaRegion); float maxValue = itk::NumericTraits::min(); float minValue = itk::NumericTraits::max(); typename ImageType::IndexType maxIndex; typename ImageType::IndexType minIndex; for(unsigned short i = 0; i < VImageDimension; ++i) { maxIndex[i] = 0; minIndex[i] = 0; } if (maskImage != nullptr) { MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion()); typename ImageType::IndexType imageIndex; typename ImageType::PointType worldPosition; typename ImageType::IndexType maskIndex; for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt) { imageIndex = maskIndex = maskIt.GetIndex(); if(maskIt.Get() == label) { if( allowedExtremaRegion.IsInside(imageIndex) ) { imageIndexIt.SetIndex( imageIndex ); double value = imageIndexIt.Get(); minMax.Defined = true; //Calculate minimum, maximum and corresponding index-values if( value > maxValue ) { maxIndex = imageIndexIt.GetIndex(); maxValue = value; } if(value < minValue ) { minIndex = imageIndexIt.GetIndex(); minValue = value; } } } } } else { for(imageIndexIt.GoToBegin(); !imageIndexIt.IsAtEnd(); ++imageIndexIt) { double value = imageIndexIt.Get(); minMax.Defined = true; //Calculate minimum, maximum and corresponding index-values if( value > maxValue ) { maxIndex = imageIndexIt.GetIndex(); maxValue = value; } if(value < minValue ) { minIndex = imageIndexIt.GetIndex(); minValue = value; } } } minMax.MaxIndex.set_size(VImageDimension); minMax.MinIndex.set_size(VImageDimension); for(unsigned int i = 0; i < minMax.MaxIndex.size(); ++i) { minMax.MaxIndex[i] = maxIndex[i]; } for(unsigned int i = 0; i < minMax.MinIndex.size(); ++i) { minMax.MinIndex[i] = minIndex[i]; } minMax.Max = maxValue; minMax.Min = minValue; return minMax; } template itk::Size HotspotMaskGenerator::CalculateConvolutionKernelSize( double spacing[VImageDimension], double radiusInMM ) { typedef itk::Image< float, VImageDimension > KernelImageType; typedef typename KernelImageType::SizeType SizeType; SizeType maskSize; for(unsigned int i = 0; i < VImageDimension; ++i) { maskSize[i] = static_cast( 2 * radiusInMM / spacing[i]); // We always want an uneven size to have a clear center point in the convolution mask if(maskSize[i] % 2 == 0 ) { ++maskSize[i]; } } return maskSize; } template itk::SmartPointer< itk::Image > HotspotMaskGenerator::GenerateHotspotSearchConvolutionKernel(double mmPerPixel[VImageDimension], double radiusInMM ) { std::stringstream ss; for (unsigned int i = 0; i < VImageDimension; ++i) { ss << mmPerPixel[i]; if (i < VImageDimension -1) ss << ","; } MITK_DEBUG << "Update convolution kernel for spacing (" << ss.str() << ") and radius " << radiusInMM << "mm"; double radiusInMMSquared = radiusInMM * radiusInMM; typedef itk::Image< float, VImageDimension > KernelImageType; typename KernelImageType::Pointer convolutionKernel = KernelImageType::New(); // Calculate size and allocate mask image typedef typename KernelImageType::SizeType SizeType; SizeType maskSize = this->CalculateConvolutionKernelSize(mmPerPixel, radiusInMM); mitk::Point3D convolutionMaskCenterIndex; convolutionMaskCenterIndex.Fill(0.0); for(unsigned int i = 0; i < VImageDimension; ++i) { convolutionMaskCenterIndex[i] = 0.5 * (double)(maskSize[i]-1); } typedef typename KernelImageType::IndexType IndexType; IndexType maskIndex; maskIndex.Fill(0); typedef typename KernelImageType::RegionType RegionType; RegionType maskRegion; maskRegion.SetSize(maskSize); maskRegion.SetIndex(maskIndex); convolutionKernel->SetRegions(maskRegion); convolutionKernel->SetSpacing(mmPerPixel); convolutionKernel->Allocate(); // Fill mask image values by subsampling the image grid typedef itk::ImageRegionIteratorWithIndex MaskIteratorType; MaskIteratorType maskIt(convolutionKernel,maskRegion); int numberOfSubVoxelsPerDimension = 2; // per dimension! int numberOfSubVoxels = ::pow( static_cast(numberOfSubVoxelsPerDimension), static_cast(VImageDimension) ); double subVoxelSizeInPixels = 1.0 / (double)numberOfSubVoxelsPerDimension; double valueOfOneSubVoxel = 1.0 / (double)numberOfSubVoxels; double maskValue = 0.0; mitk::Point3D subVoxelIndexPosition; double distanceSquared = 0.0; typedef itk::ContinuousIndex ContinuousIndexType; for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt) { ContinuousIndexType indexPoint(maskIt.GetIndex()); mitk::Point3D voxelPosition; for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension) { voxelPosition[dimension] = indexPoint[dimension]; } maskValue = 0.0; mitk::Vector3D subVoxelOffset; subVoxelOffset.Fill(0.0); // iterate sub-voxels by iterating all possible offsets for (subVoxelOffset[0] = -0.5 + subVoxelSizeInPixels / 2.0; subVoxelOffset[0] < +0.5; subVoxelOffset[0] += subVoxelSizeInPixels) { for (subVoxelOffset[1] = -0.5 + subVoxelSizeInPixels / 2.0; subVoxelOffset[1] < +0.5; subVoxelOffset[1] += subVoxelSizeInPixels) { for (subVoxelOffset[2] = -0.5 + subVoxelSizeInPixels / 2.0; subVoxelOffset[2] < +0.5; subVoxelOffset[2] += subVoxelSizeInPixels) { subVoxelIndexPosition = voxelPosition + subVoxelOffset; // this COULD be integrated into the for-loops if neccessary (add voxelPosition to initializer and end condition) distanceSquared = (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] * (subVoxelIndexPosition[0]-convolutionMaskCenterIndex[0]) * mmPerPixel[0] + (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] * (subVoxelIndexPosition[1]-convolutionMaskCenterIndex[1]) * mmPerPixel[1] + (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2] * (subVoxelIndexPosition[2]-convolutionMaskCenterIndex[2]) * mmPerPixel[2]; if (distanceSquared <= radiusInMMSquared) { maskValue += valueOfOneSubVoxel; } } } } maskIt.Set( maskValue ); } return convolutionKernel; } template itk::SmartPointer > HotspotMaskGenerator::GenerateConvolutionImage( const itk::Image* inputImage ) { double mmPerPixel[VImageDimension]; for (unsigned int dimension = 0; dimension < VImageDimension; ++dimension) { mmPerPixel[dimension] = inputImage->GetSpacing()[dimension]; } // update convolution kernel typedef itk::Image< float, VImageDimension > KernelImageType; typename KernelImageType::Pointer convolutionKernel = this->GenerateHotspotSearchConvolutionKernel(mmPerPixel, m_HotspotRadiusinMM); // update convolution image typedef itk::Image< TPixel, VImageDimension > InputImageType; typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType; typedef itk::FFTConvolutionImageFilter ConvolutionFilterType; typename ConvolutionFilterType::Pointer convolutionFilter = ConvolutionFilterType::New(); typedef itk::ConstantBoundaryCondition BoundaryConditionType; BoundaryConditionType boundaryCondition; boundaryCondition.SetConstant(0.0); if (m_HotspotMustBeCompletelyInsideImage) { // overwrite default boundary condition convolutionFilter->SetBoundaryCondition(&boundaryCondition); } convolutionFilter->SetInput(inputImage); convolutionFilter->SetKernelImage(convolutionKernel); convolutionFilter->SetNormalize(true); MITK_DEBUG << "Update Convolution image for hotspot search"; convolutionFilter->UpdateLargestPossibleRegion(); typename ConvolutionImageType::Pointer convolutionImage = convolutionFilter->GetOutput(); convolutionImage->SetSpacing( inputImage->GetSpacing() ); // only workaround because convolution filter seems to ignore spacing of input image return convolutionImage; } template < typename TPixel, unsigned int VImageDimension> void HotspotMaskGenerator::FillHotspotMaskPixels( itk::Image* maskImage, itk::Point sphereCenter, double sphereRadiusInMM ) { typedef itk::Image< TPixel, VImageDimension > MaskImageType; typedef itk::ImageRegionIteratorWithIndex MaskImageIteratorType; MaskImageIteratorType maskIt(maskImage, maskImage->GetLargestPossibleRegion()); typename MaskImageType::IndexType maskIndex; typename MaskImageType::PointType worldPosition; // this is not very smart. I would rather use a 0 initialized mask (not the case here -> blame CalculateHotspotMask) and find the region where I need to iterate over, then iterate only over the small region for(maskIt.GoToBegin(); !maskIt.IsAtEnd(); ++maskIt) { maskIndex = maskIt.GetIndex(); maskImage->TransformIndexToPhysicalPoint(maskIndex, worldPosition); maskIt.Set( worldPosition.EuclideanDistanceTo(sphereCenter) <= sphereRadiusInMM ? 1 : 0 ); } } template void HotspotMaskGenerator::CalculateHotspotMask(itk::Image* inputImage, typename itk::Image::Pointer maskImage, unsigned int label) { typedef itk::Image< TPixel, VImageDimension > InputImageType; typedef itk::Image< TPixel, VImageDimension > ConvolutionImageType; - typedef itk::Image< float, VImageDimension > KernelImageType; typedef itk::Image< unsigned short, VImageDimension > MaskImageType; typename ConvolutionImageType::Pointer convolutionImage = this->GenerateConvolutionImage(inputImage); if (convolutionImage.IsNull()) { MITK_ERROR << "Empty convolution image in CalculateHotspotStatistics(). We should never reach this state (logic error)."; throw std::logic_error("Empty convolution image in CalculateHotspotStatistics()"); } // if mask image is not defined, create an image of the same size as inputImage and fill it with 1's // there is maybe a better way to do this!? if (maskImage == nullptr) { maskImage = MaskImageType::New(); typename MaskImageType::RegionType maskRegion = inputImage->GetLargestPossibleRegion(); typename MaskImageType::SpacingType maskSpacing = inputImage->GetSpacing(); typename MaskImageType::PointType maskOrigin = inputImage->GetOrigin(); typename MaskImageType::DirectionType maskDirection = inputImage->GetDirection(); maskImage->SetRegions(maskRegion); maskImage->Allocate(); maskImage->SetOrigin(maskOrigin); maskImage->SetSpacing(maskSpacing); maskImage->SetDirection(maskDirection); maskImage->FillBuffer(1); label = 1; } // find maximum in convolution image, given the current mask double requiredDistanceToBorder = m_HotspotMustBeCompletelyInsideImage ? m_HotspotRadiusinMM : -1.0; ImageExtrema convolutionImageInformation = CalculateExtremaWorld(convolutionImage.GetPointer(), maskImage, requiredDistanceToBorder, label); bool isHotspotDefined = convolutionImageInformation.Defined; if (!isHotspotDefined) { MITK_ERROR << "No origin of hotspot-sphere was calculated!"; m_InternalMask = nullptr; } else { // create a binary mask around the "hotspot" region, fill the shape of a sphere around our hotspot center // typename DuplicatorType::Pointer copyMachine = DuplicatorType::New(); // copyMachine->SetInputImage(inputImage); // copyMachine->Update(); // typename CastFilterType::Pointer caster = CastFilterType::New(); // caster->SetInput( copyMachine->GetOutput() ); // caster->Update(); typename MaskImageType::Pointer hotspotMaskITK = MaskImageType::New(); hotspotMaskITK->SetOrigin(inputImage->GetOrigin()); hotspotMaskITK->SetSpacing(inputImage->GetSpacing()); hotspotMaskITK->SetLargestPossibleRegion(inputImage->GetLargestPossibleRegion()); hotspotMaskITK->SetBufferedRegion(inputImage->GetBufferedRegion()); hotspotMaskITK->SetDirection(inputImage->GetDirection()); hotspotMaskITK->SetNumberOfComponentsPerPixel(inputImage->GetNumberOfComponentsPerPixel()); hotspotMaskITK->Allocate(); hotspotMaskITK->FillBuffer(1); typedef typename InputImageType::IndexType IndexType; IndexType maskCenterIndex; for (unsigned int d =0; d< VImageDimension;++d) { maskCenterIndex[d]=convolutionImageInformation.MaxIndex[d]; } typename ConvolutionImageType::PointType maskCenter; inputImage->TransformIndexToPhysicalPoint(maskCenterIndex,maskCenter); FillHotspotMaskPixels(hotspotMaskITK.GetPointer(), maskCenter, m_HotspotRadiusinMM); //obtain mitk::Image::Pointer from itk::Image mitk::Image::Pointer hotspotMaskAsMITKImage = mitk::GrabItkImageMemory(hotspotMaskITK); m_InternalMask = hotspotMaskAsMITKImage; m_ConvolutionImageMaxIndex = convolutionImageInformation.MaxIndex; m_ConvolutionImageMinIndex = convolutionImageInformation.MinIndex; } } bool HotspotMaskGenerator::IsUpdateRequired() const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime(); unsigned long maskGeneratorTimeStamp = m_Mask->GetMTime(); unsigned long inputImageTimeStamp = m_inputImage->GetMTime(); if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed { return true; } if (m_InternalMaskUpdateTime < maskGeneratorTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class { return true; } if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class { return true; } return false; } } diff --git a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp index c659d8f075..71034570bd 100644 --- a/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp +++ b/Modules/ImageStatistics/mitkImageStatisticsCalculator.cpp @@ -1,642 +1,629 @@ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "itkImageFileWriter.h" namespace mitk { void ImageStatisticsCalculator::SetInputImage(mitk::Image::Pointer image) { if (image != m_Image) { m_Image = image; m_StatisticsByTimeStep.resize(m_Image->GetTimeSteps()); m_StatisticsUpdateTimePerTimeStep.resize(m_Image->GetTimeSteps()); std::fill(m_StatisticsUpdateTimePerTimeStep.begin(), m_StatisticsUpdateTimePerTimeStep.end(), 0); this->Modified(); } } void ImageStatisticsCalculator::SetMask(mitk::MaskGenerator::Pointer mask) { if (mask != m_MaskGenerator) { m_MaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetSecondaryMask(mitk::MaskGenerator::Pointer mask) { if (mask != m_SecondaryMaskGenerator) { m_SecondaryMaskGenerator = mask; this->Modified(); } } void ImageStatisticsCalculator::SetNBinsForHistogramStatistics(unsigned int nBins) { if (nBins != m_nBinsForHistogramStatistics) { m_nBinsForHistogramStatistics = nBins; this->Modified(); this->m_UseBinSizeOverNBins = false; } if (m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = false; } } unsigned int ImageStatisticsCalculator::GetNBinsForHistogramStatistics() const { return m_nBinsForHistogramStatistics; } void ImageStatisticsCalculator::SetBinSizeForHistogramStatistics(double binSize) { if (binSize != m_binSizeForHistogramStatistics) { m_binSizeForHistogramStatistics = binSize; this->Modified(); this->m_UseBinSizeOverNBins = true; } if (!m_UseBinSizeOverNBins) { this->Modified(); this->m_UseBinSizeOverNBins = true; } } double ImageStatisticsCalculator::GetBinSizeForHistogramStatistics() const { return m_binSizeForHistogramStatistics; } ImageStatisticsCalculator::StatisticsContainer::Pointer ImageStatisticsCalculator::GetStatistics(unsigned int timeStep, unsigned int label) { if (timeStep >= m_StatisticsByTimeStep.size()) { mitkThrow() << "invalid timeStep in ImageStatisticsCalculator_v2::GetStatistics"; } if (m_Image.IsNull()) { mitkThrow() << "no image"; } if (!m_Image->IsInitialized()) { mitkThrow() << "Image not initialized!"; } if (IsUpdateRequired(timeStep)) { if (m_MaskGenerator.IsNotNull()) { m_MaskGenerator->SetTimeStep(timeStep); m_InternalMask = m_MaskGenerator->GetMask(); if (m_MaskGenerator->GetReferenceImage().IsNotNull()) { m_InternalImageForStatistics = m_MaskGenerator->GetReferenceImage(); } else { m_InternalImageForStatistics = m_Image; } } else { m_InternalImageForStatistics = m_Image; } if (m_SecondaryMaskGenerator.IsNotNull()) { m_SecondaryMaskGenerator->SetTimeStep(timeStep); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(); } ImageTimeSelector::Pointer imgTimeSel = ImageTimeSelector::New(); imgTimeSel->SetInput(m_InternalImageForStatistics); imgTimeSel->SetTimeNr(timeStep); imgTimeSel->UpdateLargestPossibleRegion(); m_ImageTimeSlice = imgTimeSel->GetOutput(); // Calculate statistics with/without mask if (m_MaskGenerator.IsNull() && m_SecondaryMaskGenerator.IsNull()) { // 1) calculate statistics unmasked: AccessByItk_1(m_ImageTimeSlice, InternalCalculateStatisticsUnmasked, timeStep) } else { // 2) calculate statistics masked AccessByItk_1(m_ImageTimeSlice, InternalCalculateStatisticsMasked, timeStep) } //this->Modified(); } m_StatisticsUpdateTimePerTimeStep[timeStep] = m_StatisticsByTimeStep[timeStep][m_StatisticsByTimeStep[timeStep].size()-1]->GetMTime(); for (std::vector::iterator it = m_StatisticsByTimeStep[timeStep].begin(); it != m_StatisticsByTimeStep[timeStep].end(); ++it) { StatisticsContainer::Pointer statCont = *it; if (statCont->GetLabel() == label) { return statCont->Clone(); } } // these lines will ony be executed if the requested label could not be found! MITK_WARN << "Invalid label: " << label << " in time step: " << timeStep; return StatisticsContainer::New(); } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsUnmasked( typename itk::Image< TPixel, VImageDimension >* image, unsigned int timeStep) { typedef typename itk::Image< TPixel, VImageDimension > ImageType; typedef typename itk::ExtendedStatisticsImageFilter ImageStatisticsFilterType; typedef typename itk::MinMaxImageFilterWithIndex MinMaxFilterType; StatisticsContainer::Pointer statisticsResult = StatisticsContainer::New(); typename ImageStatisticsFilterType::Pointer statisticsFilter = ImageStatisticsFilterType::New(); statisticsFilter->SetInput(image); statisticsFilter->SetCoordinateTolerance(0.001); statisticsFilter->SetDirectionTolerance(0.001); // TODO: this is single threaded. Implement our own image filter that does this multi threaded // typename itk::MinimumMaximumImageCalculator::Pointer imgMinMaxFilter = itk::MinimumMaximumImageCalculator::New(); // imgMinMaxFilter->SetImage(image); // imgMinMaxFilter->Compute(); vnl_vector minIndex, maxIndex; typename MinMaxFilterType::Pointer minMaxFilter = MinMaxFilterType::New(); minMaxFilter->SetInput(image); minMaxFilter->UpdateLargestPossibleRegion(); typename ImageType::PixelType minval = minMaxFilter->GetMin(); typename ImageType::PixelType maxval = minMaxFilter->GetMax(); typename ImageType::IndexType tmpMinIndex = minMaxFilter->GetMinIndex(); typename ImageType::IndexType tmpMaxIndex = minMaxFilter->GetMaxIndex(); // typename ImageType::IndexType tmpMinIndex = imgMinMaxFilter->GetIndexOfMinimum(); // typename ImageType::IndexType tmpMaxIndex = imgMinMaxFilter->GetIndexOfMaximum(); minIndex.set_size(tmpMaxIndex.GetIndexDimension()); maxIndex.set_size(tmpMaxIndex.GetIndexDimension()); for (unsigned int i=0; i < tmpMaxIndex.GetIndexDimension(); i++) { minIndex[i] = tmpMinIndex[i]; maxIndex[i] = tmpMaxIndex[i]; } statisticsResult->SetMinIndex(minIndex); statisticsResult->SetMaxIndex(maxIndex); //convert m_binSize in m_nBins if necessary unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(maxval - minval)) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } statisticsFilter->SetHistogramParameters(nBinsForHistogram, minval, maxval); try { statisticsFilter->Update(); } catch (const itk::ExceptionObject& e) { mitkThrow() << "Image statistics calculation failed due to following ITK Exception: \n " << e.what(); } // no mask, therefore just one label = the whole image m_StatisticsByTimeStep[timeStep].resize(1); statisticsResult->SetLabel(1); statisticsResult->SetN(image->GetLargestPossibleRegion().GetNumberOfPixels()); statisticsResult->SetMean(statisticsFilter->GetMean()); statisticsResult->SetMin(statisticsFilter->GetMinimum()); statisticsResult->SetMax(statisticsFilter->GetMaximum()); statisticsResult->SetVariance(statisticsFilter->GetVariance()); statisticsResult->SetStd(statisticsFilter->GetSigma()); statisticsResult->SetSkewness(statisticsFilter->GetSkewness()); statisticsResult->SetKurtosis(statisticsFilter->GetKurtosis()); statisticsResult->SetRMS(std::sqrt(std::pow(statisticsFilter->GetMean(), 2.) + statisticsFilter->GetVariance())); // variance = sigma^2 statisticsResult->SetMPP(statisticsFilter->GetMPP()); statisticsResult->SetEntropy(statisticsFilter->GetEntropy()); statisticsResult->SetMedian(statisticsFilter->GetMedian()); statisticsResult->SetUniformity(statisticsFilter->GetUniformity()); statisticsResult->SetUPP(statisticsFilter->GetUPP()); statisticsResult->SetHistogram(statisticsFilter->GetHistogram()); m_StatisticsByTimeStep[timeStep][0] = statisticsResult; } template < typename TPixel, unsigned int VImageDimension > void ImageStatisticsCalculator::InternalCalculateStatisticsMasked( typename itk::Image< TPixel, VImageDimension >* image, unsigned int timeStep) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< MaskPixelType, VImageDimension > MaskType; typedef typename MaskType::PixelType LabelPixelType; typedef itk::ExtendedLabelStatisticsImageFilter< ImageType, MaskType > ImageStatisticsFilterType; typedef MaskUtilities< TPixel, VImageDimension > MaskUtilType; typedef typename itk::MinMaxLabelImageFilterWithIndex MinMaxLabelFilterType; typedef typename ImageType::PixelType InputImgPixelType; // workaround: if m_SecondaryMaskGenerator ist not null but m_MaskGenerator is! (this is the case if we request a 'ignore zuero valued pixels' // mask in the gui but do not define a primary mask) bool swapMasks = false; if (m_SecondaryMask.IsNotNull() && m_InternalMask.IsNull()) { m_InternalMask = m_SecondaryMask; m_SecondaryMask = nullptr; swapMasks = true; } // maskImage has to have the same dimension as image typename MaskType::Pointer maskImage = MaskType::New(); try { // try to access the pixel values directly (no copying or casting). Only works if mask pixels are of pixelType unsigned short maskImage = ImageToItkImage< MaskPixelType, VImageDimension >(m_InternalMask); } catch (const itk::ExceptionObject &) { // if the pixel type of the mask is not short, then we have to make a copy of m_InternalMask (and cast the values) CastToItkImage(m_InternalMask, maskImage); } // if we have a secondary mask (say a ignoreZeroPixelMask) we need to combine the masks (corresponds to AND) if (m_SecondaryMask.IsNotNull()) { // dirty workaround for a bug when pf mask + any other mask is used in conjunction. We need a proper fix for this (Fabian Isensee is responsible and probably working on it!) if (m_InternalMask->GetDimension() == 2 && (m_SecondaryMask->GetDimension() == 3 || m_SecondaryMask->GetDimension() == 4)) { mitk::Image::Pointer old_img = m_SecondaryMaskGenerator->GetReferenceImage(); m_SecondaryMaskGenerator->SetInputImage(m_MaskGenerator->GetReferenceImage()); m_SecondaryMask = m_SecondaryMaskGenerator->GetMask(); m_SecondaryMaskGenerator->SetInputImage(old_img); } typename MaskType::Pointer secondaryMaskImage = MaskType::New(); secondaryMaskImage = ImageToItkImage< MaskPixelType, VImageDimension >(m_SecondaryMask); // secondary mask should be a ignore zero value pixel mask derived from image. it has to be cropped to the mask region (which may be planar or simply smaller) typename MaskUtilities::Pointer secondaryMaskMaskUtil = MaskUtilities::New(); secondaryMaskMaskUtil->SetImage(secondaryMaskImage.GetPointer()); secondaryMaskMaskUtil->SetMask(maskImage.GetPointer()); typename MaskType::Pointer adaptedSecondaryMaskImage = secondaryMaskMaskUtil->ExtractMaskImageRegion(); typename itk::MaskImageFilter2::Pointer maskFilter = itk::MaskImageFilter2::New(); maskFilter->SetInput1(maskImage); maskFilter->SetInput2(adaptedSecondaryMaskImage); maskFilter->SetMaskingValue(1); // all pixels of maskImage where secondaryMaskImage==1 will be kept, all the others are set to 0 maskFilter->UpdateLargestPossibleRegion(); maskImage = maskFilter->GetOutput(); } typename MaskUtilType::Pointer maskUtil = MaskUtilType::New(); maskUtil->SetImage(image); maskUtil->SetMask(maskImage.GetPointer()); // if mask is smaller than image, extract the image region where the mask is typename ImageType::Pointer adaptedImage = ImageType::New(); adaptedImage = maskUtil->ExtractMaskImageRegion(); // this also checks mask sanity // find min, max, minindex and maxindex typename MinMaxLabelFilterType::Pointer minMaxFilter = MinMaxLabelFilterType::New(); minMaxFilter->SetInput(adaptedImage); minMaxFilter->SetLabelInput(maskImage); minMaxFilter->UpdateLargestPossibleRegion(); // set histogram parameters for each label individually (min/max may be different for each label) typedef typename std::map MapType; typedef typename std::pair PairType; std::vector relevantLabels = minMaxFilter->GetRelevantLabels(); MapType minVals; MapType maxVals; std::map nBins; for (LabelPixelType label:relevantLabels) { minVals.insert(PairType(label, minMaxFilter->GetMin(label))); maxVals.insert(PairType(label, minMaxFilter->GetMax(label))); unsigned int nBinsForHistogram; if (m_UseBinSizeOverNBins) { nBinsForHistogram = std::max(static_cast(std::ceil(minMaxFilter->GetMax(label) - minMaxFilter->GetMin(label))) / m_binSizeForHistogramStatistics, 10.); // do not allow less than 10 bins } else { nBinsForHistogram = m_nBinsForHistogramStatistics; } nBins.insert(typename std::pair(label, nBinsForHistogram)); } typename ImageStatisticsFilterType::Pointer imageStatisticsFilter = ImageStatisticsFilterType::New(); imageStatisticsFilter->SetDirectionTolerance(0.001); imageStatisticsFilter->SetCoordinateTolerance(0.001); imageStatisticsFilter->SetInput(adaptedImage); imageStatisticsFilter->SetLabelInput(maskImage); imageStatisticsFilter->SetHistogramParametersForLabels(nBins, minVals, maxVals); imageStatisticsFilter->Update(); std::list labels = imageStatisticsFilter->GetRelevantLabels(); std::list::iterator it = labels.begin(); m_StatisticsByTimeStep[timeStep].resize(0); while(it != labels.end()) { StatisticsContainer::Pointer statisticsResult = StatisticsContainer::New(); // find min, max, minindex and maxindex // make sure to only look in the masked region, use a masker for this vnl_vector minIndex, maxIndex; mitk::Point3D worldCoordinateMin; mitk::Point3D worldCoordinateMax; mitk::Point3D indexCoordinateMin; mitk::Point3D indexCoordinateMax; m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMinIndex(*it), worldCoordinateMin); m_InternalImageForStatistics->GetGeometry()->IndexToWorld(minMaxFilter->GetMaxIndex(*it), worldCoordinateMax); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMin, indexCoordinateMin); m_Image->GetGeometry()->WorldToIndex(worldCoordinateMax, indexCoordinateMax); - //typename ImageType::IndexType tmpMinIndex = minMaxFilter->GetMinIndex(*it); - //typename ImageType::IndexType tmpMaxIndex = minMaxFilter->GetMaxIndex(*it); - - //minIndex.set_size(tmpMaxIndex.GetIndexDimension()); - //maxIndex.set_size(tmpMaxIndex.GetIndexDimension()); minIndex.set_size(3); maxIndex.set_size(3); //for (unsigned int i=0; i < tmpMaxIndex.GetIndexDimension(); i++) for (unsigned int i=0; i < 3; i++) { - //minIndex[i] = tmpMinIndex[i] + (maskImage->GetOrigin()[i] - image->GetOrigin()[i]) / (double) maskImage->GetSpacing()[i]; - //maxIndex[i] = tmpMaxIndex[i] + (maskImage->GetOrigin()[i] - image->GetOrigin()[i]) / (double) maskImage->GetSpacing()[i]; minIndex[i] = indexCoordinateMin[i]; maxIndex[i] = indexCoordinateMax[i]; } statisticsResult->SetMinIndex(minIndex); statisticsResult->SetMaxIndex(maxIndex); - // just debug - TPixel min_Filter = minMaxFilter->GetMin(*it); - TPixel max_Filter = minMaxFilter->GetMax(*it); - TPixel min_Itk = imageStatisticsFilter->GetMinimum(*it); - TPixel max_Itk = imageStatisticsFilter->GetMaximum(*it); - assert(abs(minMaxFilter->GetMax(*it) - imageStatisticsFilter->GetMaximum(*it)) < mitk::eps); assert(abs(minMaxFilter->GetMin(*it) - imageStatisticsFilter->GetMinimum(*it)) < mitk::eps); statisticsResult->SetN(imageStatisticsFilter->GetSum(*it) / (double) imageStatisticsFilter->GetMean(*it)); statisticsResult->SetMean(imageStatisticsFilter->GetMean(*it)); statisticsResult->SetMin(imageStatisticsFilter->GetMinimum(*it)); statisticsResult->SetMax(imageStatisticsFilter->GetMaximum(*it)); statisticsResult->SetVariance(imageStatisticsFilter->GetVariance(*it)); statisticsResult->SetStd(imageStatisticsFilter->GetSigma(*it)); statisticsResult->SetSkewness(imageStatisticsFilter->GetSkewness(*it)); statisticsResult->SetKurtosis(imageStatisticsFilter->GetKurtosis(*it)); statisticsResult->SetRMS(std::sqrt(std::pow(imageStatisticsFilter->GetMean(*it), 2.) + imageStatisticsFilter->GetVariance(*it))); // variance = sigma^2 statisticsResult->SetMPP(imageStatisticsFilter->GetMPP(*it)); statisticsResult->SetLabel(*it); statisticsResult->SetEntropy(imageStatisticsFilter->GetEntropy(*it)); statisticsResult->SetMedian(imageStatisticsFilter->GetMedian(*it)); statisticsResult->SetUniformity(imageStatisticsFilter->GetUniformity(*it)); statisticsResult->SetUPP(imageStatisticsFilter->GetUPP(*it)); statisticsResult->SetHistogram(imageStatisticsFilter->GetHistogram(*it)); m_StatisticsByTimeStep[timeStep].push_back(statisticsResult); ++it; } // swap maskGenerators back if (swapMasks) { m_SecondaryMask = m_InternalMask; m_InternalMask = nullptr; } } bool ImageStatisticsCalculator::IsUpdateRequired(unsigned int timeStep) const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long inputImageTimeStamp = m_Image->GetMTime(); unsigned long statisticsTimeStamp = m_StatisticsUpdateTimePerTimeStep[timeStep]; if (thisClassTimeStamp > statisticsTimeStamp) // inputs have changed { return true; } if (inputImageTimeStamp > statisticsTimeStamp) // image has changed { return true; } if (m_MaskGenerator.IsNotNull()) { unsigned long maskGeneratorTimeStamp = m_MaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a mask generator and it has changed { return true; } } if (m_SecondaryMaskGenerator.IsNotNull()) { unsigned long maskGeneratorTimeStamp = m_SecondaryMaskGenerator->GetMTime(); if (maskGeneratorTimeStamp > statisticsTimeStamp) // there is a secondary mask generator and it has changed { return true; } } return false; } ImageStatisticsCalculator::StatisticsContainer::StatisticsContainer(): - m_N(nan("")), + m_N(0), m_Mean(nan("")), m_Min(nan("")), m_Max(nan("")), m_Std(nan("")), m_Variance(nan("")), m_Skewness(nan("")), m_Kurtosis(nan("")), m_RMS(nan("")), m_MPP(nan("")), m_Median(nan("")), m_Uniformity(nan("")), m_UPP(nan("")), m_Entropy(nan("")) { m_minIndex.set_size(0); m_maxIndex.set_size(0); } ImageStatisticsCalculator::statisticsMapType ImageStatisticsCalculator::StatisticsContainer::GetStatisticsAsMap() { ImageStatisticsCalculator::statisticsMapType statisticsAsMap; statisticsAsMap["N"] = m_N; statisticsAsMap["Mean"] = m_Mean; statisticsAsMap["Min"] = m_Min; statisticsAsMap["Max"] = m_Max; statisticsAsMap["StandardDeviation"] = m_Std; statisticsAsMap["Variance"] = m_Variance; statisticsAsMap["Skewness"] = m_Skewness; statisticsAsMap["Kurtosis"] = m_Kurtosis; statisticsAsMap["RMS"] = m_RMS; statisticsAsMap["MPP"] = m_MPP; statisticsAsMap["Median"] = m_Median; statisticsAsMap["Uniformity"] = m_Uniformity; statisticsAsMap["UPP"] = m_UPP; statisticsAsMap["Entropy"] = m_Entropy; statisticsAsMap["Label"] = m_Label; return statisticsAsMap; } void ImageStatisticsCalculator::StatisticsContainer::Reset() { - m_N = nan(""); + m_N = 0; m_Mean = nan(""); m_Min = nan(""); m_Max = nan(""); m_Std = nan(""); m_Variance = nan(""); m_Skewness = nan(""); m_Kurtosis = nan(""); m_RMS = nan(""); m_MPP = nan(""); m_Median = nan(""); m_Uniformity = nan(""); m_UPP = nan(""); m_Entropy = nan(""); m_Histogram = HistogramType::New(); m_minIndex.set_size(0); m_maxIndex.set_size(0); m_Label = 0; } void ImageStatisticsCalculator::StatisticsContainer::Print() { ImageStatisticsCalculator::statisticsMapType statMap = this->GetStatisticsAsMap(); // print all map key value pairs // const auto& val:statMap for (auto it = statMap.begin(); it != statMap.end(); ++it) { std::cout << it->first << ": " << it->second << std::endl; } // print the min and max index std::cout << "Min Index:" << std::endl; for (auto it = this->GetMinIndex().begin(); it != this->GetMinIndex().end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; // print the min and max index std::cout << "Max Index:" << std::endl; for (auto it = this->GetMaxIndex().begin(); it != this->GetMaxIndex().end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; } std::string ImageStatisticsCalculator::StatisticsContainer::GetAsString() { std::string res = ""; ImageStatisticsCalculator::statisticsMapType statMap = this->GetStatisticsAsMap(); // print all map key value pairs // const auto& val:statMap for (auto it = statMap.begin(); it != statMap.end(); ++it) { res += std::string(it->first) + ": " + std::to_string(it->second) + "\n"; } // print the min and max index res += "Min Index:" + std::string("\n"); for (auto it = this->GetMinIndex().begin(); it != this->GetMinIndex().end(); it++) { res += std::to_string(*it) + std::string(" "); } res += "\n"; // print the min and max index res += "Max Index:" + std::string("\n"); for (auto it = this->GetMaxIndex().begin(); it != this->GetMaxIndex().end(); it++) { res += std::to_string(*it) + " "; } res += "\n"; return res; } } diff --git a/Modules/ImageStatistics/mitkMaskUtilities.cpp b/Modules/ImageStatistics/mitkMaskUtilities.cpp index 38f82022ac..9da8e3b2aa 100644 --- a/Modules/ImageStatistics/mitkMaskUtilities.cpp +++ b/Modules/ImageStatistics/mitkMaskUtilities.cpp @@ -1,189 +1,189 @@ #ifndef MITKMASKUTIL_CPP #define MITKMASKUTIL_CPP #include //#include #include #include #include #include namespace mitk { template void MaskUtilities::SetImage(ImageType* image) { if (image != m_Image) { m_Image = image; } } template void MaskUtilities::SetMask(MaskType* mask) { if (mask != m_Mask) { m_Mask = mask; } } template bool MaskUtilities::CheckMaskSanity() { if (m_Mask==nullptr || m_Image==nullptr) { MITK_ERROR << "Set an image and a mask first"; } typedef itk::Image< TPixel, VImageDimension > ImageType; typedef typename ImageType::PointType PointType; typedef typename ImageType::DirectionType DirectionType; bool maskSanity = true; if (m_Mask==nullptr) { MITK_ERROR << "Something went wrong when casting the mitk mask image to an itk mask image. Do the mask and the input image have the same dimension?"; // note to self: We could try to convert say a 2d mask to a 3d mask if the image is 3d. (mask and image dimension have to match.) } // check direction DirectionType imageDirection = m_Image->GetDirection(); DirectionType maskDirection = m_Mask->GetDirection(); - for( int i = 0; i < imageDirection.ColumnDimensions; ++i ) + for(unsigned int i = 0; i < imageDirection.ColumnDimensions; ++i ) { - for( int j = 0; j < imageDirection.ColumnDimensions; ++j ) + for(unsigned int j = 0; j < imageDirection.ColumnDimensions; ++j ) { double differenceDirection = imageDirection[i][j] - maskDirection[i][j]; if ( fabs( differenceDirection ) > mitk::eps ) { double differenceDirection = imageDirection[i][j] - maskDirection[i][j]; if ( fabs( differenceDirection ) > 0.001 /*mitk::eps*/ ) // TODO: temp fix (bug 17121) { maskSanity = false; MITK_INFO << "Mask needs to have same direction as image! (Image direction: " << imageDirection << "; Mask direction: " << maskDirection << ")"; } } } } // check spacing PointType imageSpacing = m_Image->GetSpacing(); PointType maskSpacing = m_Mask->GetSpacing(); for (unsigned int i = 0; i < VImageDimension; i++) { if ( fabs( maskSpacing[i] - imageSpacing[i] ) > mitk::eps ) { maskSanity = false; MITK_INFO << "Spacing of mask and image is not equal. Mask: " << maskSpacing << " image: " << imageSpacing; } } // check alignment // Make sure that the voxels of mask and image are correctly "aligned", i.e., voxel boundaries are the same in both images PointType imageOrigin = m_Image->GetOrigin(); PointType maskOrigin = m_Mask->GetOrigin(); typedef itk::ContinuousIndex ContinousIndexType; ContinousIndexType maskOriginContinousIndex, imageOriginContinousIndex; m_Image->TransformPhysicalPointToContinuousIndex(maskOrigin, maskOriginContinousIndex); m_Image->TransformPhysicalPointToContinuousIndex(imageOrigin, imageOriginContinousIndex); for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i ) { double misalignment = maskOriginContinousIndex[i] - floor( maskOriginContinousIndex[i] + 0.5 ); // misalignment must be a multiple (int) of spacing in that direction if ( fmod(misalignment,imageSpacing[i]) > mitk::eps ) { maskSanity = false; MITK_INFO << "Pixels/voxels of mask and image are not sufficiently aligned! (Misalignment: " << fmod(misalignment,imageSpacing[i]) << ")"; } } // mask must be completely inside image region // Make sure that mask region is contained within image region if ( m_Mask!=nullptr && !m_Image->GetLargestPossibleRegion().IsInside( m_Mask->GetLargestPossibleRegion() ) ) { maskSanity = false; MITK_INFO << "Mask region needs to be inside of image region! (Image region: " << m_Image->GetLargestPossibleRegion() << "; Mask region: " << m_Mask->GetLargestPossibleRegion() << ")"; } return maskSanity; } template typename itk::Image::Pointer MaskUtilities::ExtractMaskImageRegion() { if (m_Mask==nullptr || m_Image==nullptr) { MITK_ERROR << "Set an image and a mask first"; } bool maskSanity = CheckMaskSanity(); if (!maskSanity) { MITK_ERROR << "Mask and image are not compatible"; } typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, VImageDimension > MaskType; typedef itk::ExtractImageFilter< ImageType, ImageType > ExtractImageFilterType; typename ImageType::SizeType imageSize = m_Image->GetBufferedRegion().GetSize(); typename ImageType::SizeType maskSize = m_Mask->GetBufferedRegion().GetSize(); typename itk::Image::Pointer extractedImg = itk::Image::New(); bool maskSmallerImage = false; for ( unsigned int i = 0; i < ImageType::ImageDimension; ++i ) { if ( maskSize[i] < imageSize[i] ) { maskSmallerImage = true; } } if ( maskSmallerImage ) { typename ExtractImageFilterType::Pointer extractImageFilter = ExtractImageFilterType::New(); typename MaskType::PointType maskOrigin = m_Mask->GetOrigin(); typename ImageType::PointType imageOrigin = m_Image->GetOrigin(); typename MaskType::SpacingType maskSpacing = m_Mask->GetSpacing(); typename ImageType::RegionType extractionRegion; typename ImageType::IndexType extractionRegionIndex; for (unsigned int i=0; i < maskOrigin.GetPointDimension(); i++) { extractionRegionIndex[i] = (maskOrigin[i] - imageOrigin[i]) / maskSpacing[i]; } extractionRegion.SetIndex(extractionRegionIndex); extractionRegion.SetSize(m_Mask->GetLargestPossibleRegion().GetSize()); extractImageFilter->SetInput( m_Image ); extractImageFilter->SetExtractionRegion( extractionRegion ); extractImageFilter->SetCoordinateTolerance( 0.001 ); extractImageFilter->SetDirectionTolerance( 0.001 ); extractImageFilter->Update(); extractedImg = extractImageFilter->GetOutput(); extractedImg->SetOrigin(m_Mask->GetOrigin()); extractedImg->SetLargestPossibleRegion(m_Mask->GetLargestPossibleRegion()); extractedImg->SetBufferedRegion(m_Mask->GetBufferedRegion()); } else { extractedImg = m_Image; } return extractedImg; } } #endif diff --git a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp index 4f7b19990d..d6d4cddcd6 100644 --- a/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp +++ b/Modules/ImageStatistics/mitkPlanarFigureMaskGenerator.cpp @@ -1,426 +1,425 @@ #include #include #include #include "mitkImageAccessByItk.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { void PlanarFigureMaskGenerator::SetPlanarFigure(mitk::PlanarFigure::Pointer planarFigure) { if ( planarFigure.IsNull() ) { throw std::runtime_error( "Error: planar figure empty!" ); } if ( !planarFigure->IsClosed() ) { throw std::runtime_error( "Masking not possible for non-closed figures" ); } const PlaneGeometry *planarFigurePlaneGeometry = planarFigure->GetPlaneGeometry(); if ( planarFigurePlaneGeometry == nullptr ) { throw std::runtime_error( "Planar-Figure not yet initialized!" ); } const PlaneGeometry *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); if ( planarFigureGeometry == nullptr ) { throw std::runtime_error( "Non-planar planar figures not supported!" ); } if (planarFigure != m_PlanarFigure) { this->Modified(); m_PlanarFigure = planarFigure; } } mitk::Image::Pointer PlanarFigureMaskGenerator::GetReferenceImage() { if (IsUpdateRequired()) { this->CalculateMask(); } return m_ReferenceImage; } template < typename TPixel, unsigned int VImageDimension > void PlanarFigureMaskGenerator::InternalCalculateMaskFromPlanarFigure( const itk::Image< TPixel, VImageDimension > *image, unsigned int axis ) { - typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< unsigned short, 2 > MaskImage2DType; typename MaskImage2DType::Pointer maskImage = MaskImage2DType::New(); maskImage->SetOrigin(image->GetOrigin()); maskImage->SetSpacing(image->GetSpacing()); maskImage->SetLargestPossibleRegion(image->GetLargestPossibleRegion()); maskImage->SetBufferedRegion(image->GetBufferedRegion()); maskImage->SetDirection(image->GetDirection()); maskImage->SetNumberOfComponentsPerPixel(image->GetNumberOfComponentsPerPixel()); maskImage->Allocate(); maskImage->FillBuffer(1); // all PolylinePoints of the PlanarFigure are stored in a vtkPoints object. // These points are used by the vtkLassoStencilSource to create // a vtkImageStencil. const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const typename PlanarFigure::PolyLineType planarFigurePolyline = m_PlanarFigure->GetPolyLine( 0 ); const mitk::BaseGeometry *imageGeometry3D = m_inputImage->GetGeometry( 0 ); // If there is a second poly line in a closed planar figure, treat it as a hole. PlanarFigure::PolyLineType planarFigureHolePolyline; if (m_PlanarFigure->GetPolyLinesSize() == 2) planarFigureHolePolyline = m_PlanarFigure->GetPolyLine(1); // Determine x- and y-dimensions depending on principal axis // TODO use plane geometry normal to determine that automatically, then check whether the PF is aligned with one of the three principal axis int i0, i1; switch ( axis ) { case 0: i0 = 1; i1 = 2; break; case 1: i0 = 0; i1 = 2; break; case 2: default: i0 = 0; i1 = 1; break; } // store the polyline contour as vtkPoints object bool outOfBounds = false; vtkSmartPointer points = vtkSmartPointer::New(); typename PlanarFigure::PolyLineType::const_iterator it; for ( it = planarFigurePolyline.begin(); it != planarFigurePolyline.end(); ++it ) { Point3D point3D; // Convert 2D point back to the local index coordinates of the selected // image // Fabian: From PlaneGeometry documentation: // Converts a 2D point given in mm (pt2d_mm) relative to the upper-left corner of the geometry into the corresponding world-coordinate (a 3D point in mm, pt3d_mm). // To convert a 2D point given in units (e.g., pixels in case of an image) into a 2D point given in mm (as required by this method), use IndexToWorld. planarFigurePlaneGeometry->Map( *it, point3D ); // Polygons (partially) outside of the image bounds can not be processed // further due to a bug in vtkPolyDataToImageStencil if ( !imageGeometry3D->IsInside( point3D ) ) { outOfBounds = true; } imageGeometry3D->WorldToIndex( point3D, point3D ); points->InsertNextPoint( point3D[i0], point3D[i1], 0 ); } vtkSmartPointer holePoints = nullptr; if (!planarFigureHolePolyline.empty()) { holePoints = vtkSmartPointer::New(); Point3D point3D; PlanarFigure::PolyLineType::const_iterator end = planarFigureHolePolyline.end(); for (it = planarFigureHolePolyline.begin(); it != end; ++it) { // Fabian: same as above planarFigurePlaneGeometry->Map(*it, point3D); imageGeometry3D->WorldToIndex(point3D, point3D); holePoints->InsertNextPoint(point3D[i0], point3D[i1], 0); } } // mark a malformed 2D planar figure ( i.e. area = 0 ) as out of bounds // this can happen when all control points of a rectangle lie on the same line = two of the three extents are zero double bounds[6] = {0, 0, 0, 0, 0, 0}; points->GetBounds( bounds ); bool extent_x = (fabs(bounds[0] - bounds[1])) < mitk::eps; bool extent_y = (fabs(bounds[2] - bounds[3])) < mitk::eps; bool extent_z = (fabs(bounds[4] - bounds[5])) < mitk::eps; // throw an exception if a closed planar figure is deformed, i.e. has only one non-zero extent if ( m_PlanarFigure->IsClosed() && ((extent_x && extent_y) || (extent_x && extent_z) || (extent_y && extent_z))) { mitkThrow() << "Figure has a zero area and cannot be used for masking."; } if ( outOfBounds ) { throw std::runtime_error( "Figure at least partially outside of image bounds!" ); } // create a vtkLassoStencilSource and set the points of the Polygon vtkSmartPointer lassoStencil = vtkSmartPointer::New(); lassoStencil->SetShapeToPolygon(); lassoStencil->SetPoints( points ); vtkSmartPointer holeLassoStencil = nullptr; if (holePoints.GetPointer() != nullptr) { holeLassoStencil = vtkSmartPointer::New(); holeLassoStencil->SetShapeToPolygon(); holeLassoStencil->SetPoints(holePoints); } // Export from ITK to VTK (to use a VTK filter) typedef itk::VTKImageImport< MaskImage2DType > ImageImportType; typedef itk::VTKImageExport< MaskImage2DType > ImageExportType; typename ImageExportType::Pointer itkExporter = ImageExportType::New(); itkExporter->SetInput( maskImage ); // itkExporter->SetInput( castFilter->GetOutput() ); vtkSmartPointer vtkImporter = vtkSmartPointer::New(); this->ConnectPipelines( itkExporter, vtkImporter ); // Apply the generated image stencil to the input image vtkSmartPointer imageStencilFilter = vtkSmartPointer::New(); imageStencilFilter->SetInputConnection( vtkImporter->GetOutputPort() ); imageStencilFilter->SetStencilConnection(lassoStencil->GetOutputPort()); imageStencilFilter->ReverseStencilOff(); imageStencilFilter->SetBackgroundValue( 0 ); imageStencilFilter->Update(); vtkSmartPointer holeStencilFilter = nullptr; if (holeLassoStencil.GetPointer() != nullptr) { holeStencilFilter = vtkSmartPointer::New(); holeStencilFilter->SetInputConnection(imageStencilFilter->GetOutputPort()); holeStencilFilter->SetStencilConnection(holeLassoStencil->GetOutputPort()); holeStencilFilter->ReverseStencilOn(); holeStencilFilter->SetBackgroundValue(0); holeStencilFilter->Update(); } // Export from VTK back to ITK vtkSmartPointer vtkExporter = vtkSmartPointer::New(); vtkExporter->SetInputConnection( holeStencilFilter.GetPointer() == nullptr ? imageStencilFilter->GetOutputPort() : holeStencilFilter->GetOutputPort()); vtkExporter->Update(); typename ImageImportType::Pointer itkImporter = ImageImportType::New(); this->ConnectPipelines( vtkExporter, itkImporter ); itkImporter->Update(); typedef itk::ImageDuplicator< ImageImportType::OutputImageType > DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage( itkImporter->GetOutput() ); duplicator->Update(); // Store mask m_InternalITKImageMask2D = duplicator->GetOutput(); } bool PlanarFigureMaskGenerator::GetPrincipalAxis( const BaseGeometry *geometry, Vector3D vector, unsigned int &axis ) { vector.Normalize(); for ( unsigned int i = 0; i < 3; ++i ) { Vector3D axisVector = geometry->GetAxisVector( i ); axisVector.Normalize(); if ( fabs( fabs( axisVector * vector ) - 1.0) < mitk::eps ) { axis = i; return true; } } return false; } void PlanarFigureMaskGenerator::CalculateMask() { if (m_inputImage.IsNull()) { MITK_ERROR << "Image is not set."; } if (m_PlanarFigure.IsNull()) { MITK_ERROR << "PlanarFigure is not set."; } if (m_TimeStep != 0) { MITK_WARN << "Multiple TimeSteps are not supported in PlanarFigureMaskGenerator (yet)."; } const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); if ( imageGeometry == nullptr ) { throw std::runtime_error( "Image geometry invalid!" ); } if (m_inputImage->GetTimeSteps() > 0) { mitk::ImageTimeSelector::Pointer imgTimeSel = mitk::ImageTimeSelector::New(); imgTimeSel->SetInput(m_inputImage); imgTimeSel->SetTimeNr(m_TimeStep); imgTimeSel->UpdateLargestPossibleRegion(); m_InternalTimeSliceImage = imgTimeSel->GetOutput(); } else { m_InternalTimeSliceImage = m_inputImage; } m_InternalITKImageMask2D = nullptr; const PlaneGeometry *planarFigurePlaneGeometry = m_PlanarFigure->GetPlaneGeometry(); const PlaneGeometry *planarFigureGeometry = dynamic_cast< const PlaneGeometry * >( planarFigurePlaneGeometry ); //const BaseGeometry *imageGeometry = m_inputImage->GetGeometry(); // Find principal direction of PlanarFigure in input image unsigned int axis; if ( !this->GetPrincipalAxis( imageGeometry, planarFigureGeometry->GetNormal(), axis ) ) { throw std::runtime_error( "Non-aligned planar figures not supported!" ); } m_PlanarFigureAxis = axis; // Find slice number corresponding to PlanarFigure in input image itk::Image< unsigned short, 3 >::IndexType index; imageGeometry->WorldToIndex( planarFigureGeometry->GetOrigin(), index ); unsigned int slice = index[axis]; // extract image slice which corresponds to the planarFigure and store it in m_InternalImageSlice mitk::Image::Pointer inputImageSlice = extract2DImageSlice(axis, slice); //mitk::IOUtil::SaveImage(inputImageSlice, "/home/fabian/inputSliceImage.nrrd"); // Compute mask from PlanarFigure AccessFixedDimensionByItk_1(inputImageSlice, InternalCalculateMaskFromPlanarFigure, 2, axis) //convert itk mask to mitk::Image::Pointer and return it mitk::Image::Pointer planarFigureMaskImage; planarFigureMaskImage = mitk::GrabItkImageMemory(m_InternalITKImageMask2D); //mitk::IOUtil::SaveImage(planarFigureMaskImage, "/home/fabian/planarFigureMaskImage.nrrd"); //Convert2Dto3DImageFilter::Pointer sliceTo3DImageConverter = Convert2Dto3DImageFilter::New(); //sliceTo3DImageConverter->SetInput(planarFigureMaskImage); //sliceTo3DImageConverter->Update(); //mitk::IOUtil::SaveImage(sliceTo3DImageConverter->GetOutput(), "/home/fabian/3DsliceImage.nrrd"); m_ReferenceImage = inputImageSlice; //mitk::IOUtil::SaveImage(m_ReferenceImage, "/home/fabian/referenceImage.nrrd"); m_InternalMask = planarFigureMaskImage; } void PlanarFigureMaskGenerator::SetTimeStep(unsigned int timeStep) { if (timeStep != m_TimeStep) { m_TimeStep = timeStep; } } mitk::Image::Pointer PlanarFigureMaskGenerator::GetMask() { if (IsUpdateRequired()) { this->CalculateMask(); this->Modified(); } m_InternalMaskUpdateTime = this->GetMTime(); return m_InternalMask; } mitk::Image::Pointer PlanarFigureMaskGenerator::extract2DImageSlice(unsigned int axis, unsigned int slice) { // Extract slice with given position and direction from image unsigned int dimension = m_InternalTimeSliceImage->GetDimension(); mitk::Image::Pointer imageSlice = mitk::Image::New(); if (dimension == 3) { ExtractImageFilter::Pointer imageExtractor = ExtractImageFilter::New(); imageExtractor->SetInput( m_InternalTimeSliceImage ); imageExtractor->SetSliceDimension( axis ); imageExtractor->SetSliceIndex( slice ); imageExtractor->Update(); imageSlice = imageExtractor->GetOutput(); } else if(dimension == 2) { imageSlice = m_InternalTimeSliceImage; } else { MITK_ERROR << "Unsupported image dimension. Dimension is: " << dimension << ". Only 2D and 3D images are supported."; } return imageSlice; } bool PlanarFigureMaskGenerator::IsUpdateRequired() const { unsigned long thisClassTimeStamp = this->GetMTime(); unsigned long internalMaskTimeStamp = m_InternalMask->GetMTime(); unsigned long planarFigureTimeStamp = m_PlanarFigure->GetMTime(); unsigned long inputImageTimeStamp = m_inputImage->GetMTime(); if (thisClassTimeStamp > m_InternalMaskUpdateTime) // inputs have changed { return true; } if (m_InternalMaskUpdateTime < planarFigureTimeStamp || m_InternalMaskUpdateTime < inputImageTimeStamp) // mask image has changed outside of this class { return true; } if (internalMaskTimeStamp > m_InternalMaskUpdateTime) // internal mask has been changed outside of this class { return true; } return false; } } diff --git a/Modules/OpenViewCore/include/QVTKFramebufferObjectRenderer.h b/Modules/OpenViewCore/include/QVTKFramebufferObjectRenderer.h index 3fba37532b..492d09cff3 100644 --- a/Modules/OpenViewCore/include/QVTKFramebufferObjectRenderer.h +++ b/Modules/OpenViewCore/include/QVTKFramebufferObjectRenderer.h @@ -1,50 +1,50 @@ /*=================================================================== 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 __QVTKFramebufferObjectRenderer_h #define __QVTKFramebufferObjectRenderer_h #include #include "QVTKQuickItem.h" #include class vtkInternalOpenGLRenderWindow; //! Part of Qml rendering prototype, see QmlMitkRenderWindowItem. class MITKOPENVIEWCORE_EXPORT QVTKFramebufferObjectRenderer : public QQuickFramebufferObject::Renderer { public: + vtkInternalOpenGLRenderWindow *m_vtkRenderWindow; bool m_neverRendered; bool m_readyToRender; - vtkInternalOpenGLRenderWindow *m_vtkRenderWindow; QVTKQuickItem *m_vtkQuickItem; public: QVTKFramebufferObjectRenderer(vtkInternalOpenGLRenderWindow *rw); ~QVTKFramebufferObjectRenderer(); virtual void synchronize(QQuickFramebufferObject * item); virtual void render(); QOpenGLFramebufferObject *createFramebufferObject(const QSize &size); friend class vtkInternalOpenGLRenderWindow; }; #endif diff --git a/Modules/Persistence/Testing/mitkPersistenceTest.cpp b/Modules/Persistence/Testing/mitkPersistenceTest.cpp index e14e19cfde..434a1d5b50 100644 --- a/Modules/Persistence/Testing/mitkPersistenceTest.cpp +++ b/Modules/Persistence/Testing/mitkPersistenceTest.cpp @@ -1,191 +1,191 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include struct PersistenceTestClass { PersistenceTestClass() : id(""), param1(1), param2(2), param3(false) {} std::string id; int param1; double param2; bool param3; PERSISTENCE_CREATE3(PersistenceTestClass, id, param1, param2, param3) }; struct TestPropertyListReplacedObserver : public mitk::PropertyListReplacedObserver { TestPropertyListReplacedObserver() : counter(0) {} - virtual void BeforePropertyListReplaced(const std::string &id, mitk::PropertyList *propertyList) override + virtual void BeforePropertyListReplaced(const std::string &id, mitk::PropertyList *) override { if (id == m_Id) - counter++; + ++counter; } - virtual void AfterPropertyListReplaced(const std::string &id, mitk::PropertyList *propertyList) override + virtual void AfterPropertyListReplaced(const std::string &id, mitk::PropertyList *) override { if (id == m_Id) - counter++; + ++counter; } int counter; std::string m_Id; }; class mitkPersistenceTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPersistenceTestSuite); MITK_TEST(PersistenceTest); CPPUNIT_TEST_SUITE_END(); private: // private test members that are initialized by setUp() std::string testClassId; int param1; double param2; bool param3; public: void setUp() override { testClassId = "testClass"; param1 = 100; param2 = 201.56; param3 = true; } void PersistenceTest() { // dummy load of SceneIO, otherwise PersistenceService won't be available mitk::PersistenceService::LoadModule(); PERSISTENCE_GET_SERVICE_MACRO CPPUNIT_ASSERT_MESSAGE("Testing availability of the PersistenceService.", persistenceService != nullptr); // Initialize testable parameter values std::string defaultPersistenceFile = persistenceService->GetDefaultPersistenceFile(); PersistenceTestClass autoLoadTestClass; autoLoadTestClass.id = testClassId; if (itksys::SystemTools::FileExists(defaultPersistenceFile.c_str(), true) && persistenceService->GetAutoLoadAndSave()) { /// Test auto load/save of the PersistenceService. itksys::SystemTools::RemoveFile(defaultPersistenceFile.c_str()); autoLoadTestClass.FromPropertyList(); testParams(autoLoadTestClass, "autoLoadTestClass"); } std::string testTempFile = mitk::IOUtil::CreateTemporaryFile("XXXXXX.mitk"); std::string testXmlTempFile = mitk::IOUtil::CreateTemporaryFile("PersistenceTestFileXXXXXX.xml"); MITK_INFO << "Testing standard write to scene file/xml file."; PersistenceTestClass testClass; testClass.id = testClassId; testClass.param1 = param1; testClass.param2 = param2; testClass.param3 = param3; CPPUNIT_ASSERT_MESSAGE("Testing to save a scene file", testClass.Save(testTempFile)); CPPUNIT_ASSERT_MESSAGE("testing to save an xml file", testClass.Save(testXmlTempFile)); CPPUNIT_ASSERT_MESSAGE("Testing read from scene file: persistenceService->RemovePropertyList(testClassId)", persistenceService->RemovePropertyList(testClassId)); PersistenceTestClass testClass2; testClass2.id = testClassId; CPPUNIT_ASSERT_MESSAGE("Testing read from scene file: testClass2.Load(testTempFile.path())", testClass2.Load(testTempFile)); testParams(testClass2, "testClass2"); CPPUNIT_ASSERT_MESSAGE("Testing read from xml file: persistenceService->RemovePropertyList(testClassId)", persistenceService->RemovePropertyList(testClassId)); PersistenceTestClass testClass3; testClass3.id = testClassId; CPPUNIT_ASSERT_MESSAGE("Testing read from xml file: testClass3.Load(testXmlTempFile.path())", testClass3.Load(testXmlTempFile)); testParams(testClass3, "testClass3"); CPPUNIT_ASSERT_MESSAGE( "Testing appendChanges functionality with scene load/write: persistenceService->RemovePropertyList(testClassId)", persistenceService->RemovePropertyList(testClassId)); CPPUNIT_ASSERT_MESSAGE( "Testing appendChanges functionality with scene load/write: persistenceService->Save(testTempFile.path())", persistenceService->Save(testTempFile, true)); CPPUNIT_ASSERT_MESSAGE( "Testing appendChanges functionality with scene load/write: persistenceService->Load(testTempFile.path())", persistenceService->Load(testTempFile)); PersistenceTestClass testClass4; testClass4.id = testClassId; testClass4.FromPropertyList(); testParams(testClass4, "testClass4"); CPPUNIT_ASSERT_MESSAGE( "Testing appendChanges functionality with xml load/write: persistenceService->RemovePropertyList(testClassId)", persistenceService->RemovePropertyList(testClassId)); CPPUNIT_ASSERT_MESSAGE( "Testing appendChanges functionality with xml load/write: persistenceService->Save(testXmlTempFile.path())", persistenceService->Save(testXmlTempFile, true)); CPPUNIT_ASSERT_MESSAGE( "Testing appendChanges functionality with xml load/write: persistenceService->Load(testXmlTempFile.path())", persistenceService->Load(testXmlTempFile)); PersistenceTestClass testClass5; testClass5.id = testClassId; testClass5.FromPropertyList(); testParams(testClass5, "testClass5"); // Test Observer Functionality TestPropertyListReplacedObserver testObserver; testObserver.m_Id = testClassId; persistenceService->AddPropertyListReplacedObserver(&testObserver); persistenceService->Load(testTempFile); CPPUNIT_ASSERT_MESSAGE( "Testing observer functionality: testObserver.counter == 2, testObserver.counter is " + testObserver.counter, testObserver.counter == 2); autoLoadTestClass.param1 = param1; autoLoadTestClass.param2 = param2; autoLoadTestClass.param3 = param3; autoLoadTestClass.ToPropertyList(); } /** * Helper Method that compares the returned class to its base values */ - void testParams(const PersistenceTestClass &testClass, const std::string &testClassName) + void testParams(const PersistenceTestClass &testClass, const std::string &) { CPPUNIT_ASSERT_MESSAGE("Parameter of TestClass not equal to reference value: testClass.id", testClass.id == testClassId); CPPUNIT_ASSERT_MESSAGE("Parameter of TestClass not equal to reference value: testClass.param1", testClass.param1 == param1); CPPUNIT_ASSERT_MESSAGE("Parameter of TestClass not equal to reference value: testClass.param2", testClass.param2 == param2); CPPUNIT_ASSERT_MESSAGE("Parameter of TestClass not equal to reference value: testClass.param3", testClass.param3 == param3); } }; MITK_TEST_SUITE_REGISTRATION(mitkPersistence) diff --git a/Modules/QmlItems/src/QmlMitkImageNavigator.cpp b/Modules/QmlItems/src/QmlMitkImageNavigator.cpp index 18b02735cc..595edb7d72 100644 --- a/Modules/QmlItems/src/QmlMitkImageNavigator.cpp +++ b/Modules/QmlItems/src/QmlMitkImageNavigator.cpp @@ -1,391 +1,379 @@ /*=================================================================== 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 "QmlMitkImageNavigator.h" #include #include QmlMitkImageNavigator* QmlMitkImageNavigator::instance = nullptr; QmlMitkImageNavigator::QmlMitkImageNavigator() : m_AxialStepper(0) , m_SagittalStepper(0) , m_FrontalStepper(0) , m_TimeStepper(0) { instance = this; } QmlMitkImageNavigator::~QmlMitkImageNavigator() { } void QmlMitkImageNavigator::setNavigatorAxial(QmlMitkSliderNavigatorItem *item) { this->m_NavigatorAxial = item; } void QmlMitkImageNavigator::setNavigatorSagittal(QmlMitkSliderNavigatorItem *item) { this->m_NavigatorSagittal = item; } void QmlMitkImageNavigator::setNavigatorCoronal(QmlMitkSliderNavigatorItem *item) { this->m_NavigatorCoronal = item; } void QmlMitkImageNavigator::setNavigatorTime(QmlMitkSliderNavigatorItem *item) { this->m_NavigatorTime = item; } QmlMitkSliderNavigatorItem* QmlMitkImageNavigator::getNavigatorAxial() { return this->m_NavigatorAxial; } QmlMitkSliderNavigatorItem* QmlMitkImageNavigator::getNavigatorSagittal() { return this->m_NavigatorSagittal; } QmlMitkSliderNavigatorItem* QmlMitkImageNavigator::getNavigatorCoronal() { return this->m_NavigatorCoronal; } QmlMitkSliderNavigatorItem* QmlMitkImageNavigator::getNavigatorTime() { return this->m_NavigatorTime; } void QmlMitkImageNavigator::setWorldCoordinateX(double coordinate) { this->m_WorldCoordinateX = coordinate; this->OnMillimetreCoordinateValueChanged(); } void QmlMitkImageNavigator::setWorldCoordinateY(double coordinate) { this->m_WorldCoordinateY = coordinate; this->OnMillimetreCoordinateValueChanged(); } void QmlMitkImageNavigator::setWorldCoordinateZ(double coordinate) { this->m_WorldCoordinateZ = coordinate; this->OnMillimetreCoordinateValueChanged(); } void QmlMitkImageNavigator::setWorldCoordinateXMin(double coordinate) { this->m_WorldCoordinateXMin = coordinate; } void QmlMitkImageNavigator::setWorldCoordinateYMin(double coordinate) { this->m_WorldCoordinateYMin = coordinate; } void QmlMitkImageNavigator::setWorldCoordinateZMin(double coordinate) { this->m_WorldCoordinateZMin = coordinate; } void QmlMitkImageNavigator::setWorldCoordinateXMax(double coordinate) { this->m_WorldCoordinateXMax = coordinate; } void QmlMitkImageNavigator::setWorldCoordinateYMax(double coordinate) { this->m_WorldCoordinateYMax = coordinate; } void QmlMitkImageNavigator::setWorldCoordinateZMax(double coordinate) { this->m_WorldCoordinateZMax = coordinate; } double QmlMitkImageNavigator::getWorldCoordinateX() { return this->m_WorldCoordinateX; } double QmlMitkImageNavigator::getWorldCoordinateY() { return this->m_WorldCoordinateY; } double QmlMitkImageNavigator::getWorldCoordinateZ() { return this->m_WorldCoordinateZ; } double QmlMitkImageNavigator::getWorldCoordinateXMin() { return this->m_WorldCoordinateXMin; } double QmlMitkImageNavigator::getWorldCoordinateYMin() { return this->m_WorldCoordinateYMin; } double QmlMitkImageNavigator::getWorldCoordinateZMin() { return this->m_WorldCoordinateZMin; } double QmlMitkImageNavigator::getWorldCoordinateXMax() { return this->m_WorldCoordinateXMax; } double QmlMitkImageNavigator::getWorldCoordinateYMax() { return this->m_WorldCoordinateYMax; } double QmlMitkImageNavigator::getWorldCoordinateZMax() { return this->m_WorldCoordinateZMax; } void QmlMitkImageNavigator::initialize() { if(!QmlMitkStdMultiItem::instance) return; QmlMitkRenderWindowItem* renderWindow = QmlMitkStdMultiItem::instance->getViewerAxial(); if (renderWindow) { if (m_AxialStepper) m_AxialStepper->deleteLater(); m_AxialStepper = new QmitkStepperAdapter(this->m_NavigatorAxial, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorAxialFromSimpleExample"); connect(m_AxialStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); } else { this->m_NavigatorAxial->setEnabled(false); } renderWindow = QmlMitkStdMultiItem::instance->getViewerSagittal(); if (renderWindow) { if (m_SagittalStepper) m_SagittalStepper->deleteLater(); m_SagittalStepper = new QmitkStepperAdapter(this->m_NavigatorSagittal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorSagittalFromSimpleExample"); connect(m_SagittalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); } else { this->m_NavigatorSagittal->setEnabled(false); } renderWindow = QmlMitkStdMultiItem::instance->getViewerCoronal(); if (renderWindow) { if (m_FrontalStepper) m_FrontalStepper->deleteLater(); m_FrontalStepper = new QmitkStepperAdapter(this->m_NavigatorCoronal, renderWindow->GetSliceNavigationController()->GetSlice(), "sliceNavigatorFrontalFromSimpleExample"); connect(m_FrontalStepper, SIGNAL(Refetch()), this, SLOT(OnRefetch())); } else { this->m_NavigatorCoronal->setEnabled(false); } mitk::SliceNavigationController* timeController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); if (timeController) { if (m_TimeStepper) m_TimeStepper->deleteLater(); m_TimeStepper = new QmitkStepperAdapter(this->m_NavigatorTime, timeController->GetTime(), "sliceNavigatorTimeFromSimpleExample"); } else { this->m_NavigatorTime->setEnabled(false); } } int QmlMitkImageNavigator::GetClosestAxisIndex(mitk::Vector3D normal) { // cos(theta) = normal . axis // cos(theta) = (a, b, c) . (d, e, f) // cos(theta) = (a, b, c) . (1, 0, 0) = a // cos(theta) = (a, b, c) . (0, 1, 0) = b // cos(theta) = (a, b, c) . (0, 0, 1) = c double absCosThetaWithAxis[3]; for (int i = 0; i < 3; i++) { absCosThetaWithAxis[i] = fabs(normal[i]); } int largestIndex = 0; double largestValue = absCosThetaWithAxis[0]; for (int i = 1; i < 3; i++) { if (absCosThetaWithAxis[i] > largestValue) { largestValue = absCosThetaWithAxis[i]; largestIndex = i; } } return largestIndex; } void QmlMitkImageNavigator::SetStepSizes() { this->SetStepSize(0); this->SetStepSize(1); this->SetStepSize(2); } void QmlMitkImageNavigator::SetStepSize(int axis) { mitk::BaseGeometry::ConstPointer geometry = QmlMitkStdMultiItem::instance->getViewerAxial()->GetSliceNavigationController()->GetInputWorldGeometry3D(); if (geometry.IsNotNull()) { mitk::Point3D crossPositionInIndexCoordinates; mitk::Point3D crossPositionInIndexCoordinatesPlus1; mitk::Point3D crossPositionInMillimetresPlus1; mitk::Vector3D transformedAxisDirection; mitk::Point3D crossPositionInMillimetres = QmlMitkStdMultiItem::instance->getCrossPosition(); geometry->WorldToIndex(crossPositionInMillimetres, crossPositionInIndexCoordinates); crossPositionInIndexCoordinatesPlus1 = crossPositionInIndexCoordinates; crossPositionInIndexCoordinatesPlus1[axis] += 1; geometry->IndexToWorld(crossPositionInIndexCoordinatesPlus1, crossPositionInMillimetresPlus1); transformedAxisDirection = crossPositionInMillimetresPlus1 - crossPositionInMillimetres; int closestAxisInMillimetreSpace = this->GetClosestAxisIndex(transformedAxisDirection); double stepSize = transformedAxisDirection.GetNorm(); this->SetStepSize(closestAxisInMillimetreSpace, stepSize); } } -void QmlMitkImageNavigator::SetStepSize(int axis, double stepSize) +void QmlMitkImageNavigator::SetStepSize(int, double) { - if (axis == 0) - { - //m_Controls.m_XWorldCoordinateSpinBox->setSingleStep(stepSize); - } - else if (axis == 1) - { - //m_Controls.m_YWorldCoordinateSpinBox->setSingleStep(stepSize); - } - else if (axis == 2) - { - //m_Controls.m_ZWorldCoordinateSpinBox->setSingleStep(stepSize); - } } void QmlMitkImageNavigator::OnMillimetreCoordinateValueChanged() { mitk::TimeGeometry::ConstPointer geometry = QmlMitkStdMultiItem::instance->getViewerAxial()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNotNull()) { mitk::Point3D positionInWorldCoordinates; positionInWorldCoordinates[0] = this->m_WorldCoordinateX; positionInWorldCoordinates[1] = this->m_WorldCoordinateY; positionInWorldCoordinates[2] = this->m_WorldCoordinateZ; QmlMitkStdMultiItem::instance->moveCrossToPosition(positionInWorldCoordinates); } } void QmlMitkImageNavigator::OnRefetch() { mitk::BaseGeometry::ConstPointer geometry = QmlMitkStdMultiItem::instance->getViewerAxial()->GetSliceNavigationController()->GetInputWorldGeometry3D(); mitk::TimeGeometry::ConstPointer timeGeometry = QmlMitkStdMultiItem::instance->getViewerAxial()->GetSliceNavigationController()->GetInputWorldTimeGeometry(); if (geometry.IsNull() && timeGeometry.IsNotNull()) { mitk::TimeStepType timeStep = QmlMitkStdMultiItem::instance->getViewerAxial()->GetSliceNavigationController()->GetTime()->GetPos(); geometry = timeGeometry->GetGeometryForTimeStep(timeStep); } if (geometry.IsNotNull()) { mitk::BoundingBox::BoundsArrayType bounds = geometry->GetBounds(); mitk::Point3D cornerPoint1InIndexCoordinates; cornerPoint1InIndexCoordinates[0] = bounds[0]; cornerPoint1InIndexCoordinates[1] = bounds[2]; cornerPoint1InIndexCoordinates[2] = bounds[4]; mitk::Point3D cornerPoint2InIndexCoordinates; cornerPoint2InIndexCoordinates[0] = bounds[1]; cornerPoint2InIndexCoordinates[1] = bounds[3]; cornerPoint2InIndexCoordinates[2] = bounds[5]; if (!geometry->GetImageGeometry()) { cornerPoint1InIndexCoordinates[0] += 0.5; cornerPoint1InIndexCoordinates[1] += 0.5; cornerPoint1InIndexCoordinates[2] += 0.5; cornerPoint2InIndexCoordinates[0] -= 0.5; cornerPoint2InIndexCoordinates[1] -= 0.5; cornerPoint2InIndexCoordinates[2] -= 0.5; } mitk::Point3D crossPositionInWorldCoordinates = QmlMitkStdMultiItem::instance->getCrossPosition(); mitk::Point3D cornerPoint1InWorldCoordinates; mitk::Point3D cornerPoint2InWorldCoordinates; geometry->IndexToWorld(cornerPoint1InIndexCoordinates, cornerPoint1InWorldCoordinates); geometry->IndexToWorld(cornerPoint2InIndexCoordinates, cornerPoint2InWorldCoordinates); this->m_WorldCoordinateXMin = std::min(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0]); this->m_WorldCoordinateYMin = std::min(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1]); this->m_WorldCoordinateZMin = std::min(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2]); this->m_WorldCoordinateXMax = std::max(cornerPoint1InWorldCoordinates[0], cornerPoint2InWorldCoordinates[0]); this->m_WorldCoordinateYMax = std::max(cornerPoint1InWorldCoordinates[1], cornerPoint2InWorldCoordinates[1]); this->m_WorldCoordinateZMax = std::max(cornerPoint1InWorldCoordinates[2], cornerPoint2InWorldCoordinates[2]); this->m_WorldCoordinateX = crossPositionInWorldCoordinates[0]; this->m_WorldCoordinateY = crossPositionInWorldCoordinates[1]; this->m_WorldCoordinateZ = crossPositionInWorldCoordinates[2]; emit this->sync(); } } void QmlMitkImageNavigator::create(QQmlEngine &engine) { QmlMitkSliderNavigatorItem::create(); qmlRegisterType("Mitk.Views", 1, 0, "ImageNavigator"); QQmlComponent component(&engine, QUrl("qrc:/MitkImageNavigator.qml")); } diff --git a/Modules/QmlItems/src/QmlMitkPiecewiseFunctionCanvas.cpp b/Modules/QmlItems/src/QmlMitkPiecewiseFunctionCanvas.cpp index 35df234279..8f9f4a1133 100644 --- a/Modules/QmlItems/src/QmlMitkPiecewiseFunctionCanvas.cpp +++ b/Modules/QmlItems/src/QmlMitkPiecewiseFunctionCanvas.cpp @@ -1,213 +1,180 @@ /*=================================================================== 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 "QmlMitkPiecewiseFunctionCanvas.h" #include QmlMitkPiecewiseFunctionCanvas* QmlMitkPiecewiseFunctionCanvas::instance = nullptr; QmlMitkPiecewiseFunctionCanvas::QmlMitkPiecewiseFunctionCanvas(QQuickPaintedItem* parent) : QmlMitkTransferFunctionCanvas(parent), m_PiecewiseFunction(nullptr) { instance = this; setAcceptedMouseButtons(Qt::AllButtons); } void QmlMitkPiecewiseFunctionCanvas::setGreyValue(double value) { this->m_GreyValue = value; this->SetX(value); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } double QmlMitkPiecewiseFunctionCanvas::getGreyValue() { return this->m_GreyValue; } void QmlMitkPiecewiseFunctionCanvas::setOpacity(double opacity) { this->m_Opacity = opacity; this->SetY(opacity); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } double QmlMitkPiecewiseFunctionCanvas::getOpacity() { return this->m_Opacity; } void QmlMitkPiecewiseFunctionCanvas::SetTitle(const QString& title) { m_Title=title; } void QmlMitkPiecewiseFunctionCanvas::paint(QPainter* painter) { PaintHistogram(painter); if (m_Title.size()>0) { painter->setPen(Qt::black); painter->drawText(QPoint(11,21),m_Title); painter->setPen(Qt::white); painter->drawText(QPoint(10,20),m_Title); } - { - - QString qs_min = QString::number( m_Min, 'g', 4 ); - QString qs_max = QString::number( m_Max, 'g', 4 ); - - QRect qr_min = painter->fontMetrics().boundingRect( qs_min ); - QRect qr_max = painter->fontMetrics().boundingRect( qs_max ); - - int y,x; - - y=this->boundingRect().height()-qr_min.height()+5; - //y = this->boundingRect().height()+5; - x=10; - - // Fill the tf presets in the generator widget - /* - painter->setPen(Qt::black); - painter->drawText(QPoint(x+1,y+1),qs_min); - painter->setPen(Qt::white); - painter->drawText(QPoint(x ,y ),qs_min); - - y=this->boundingRect().height()-qr_max.height()+5; - x=this->boundingRect().width()-qr_max.width()-6; - - painter->setPen(Qt::black); - painter->drawText(QPoint(x,y+1),qs_max); - painter->setPen(Qt::white); - painter->drawText(QPoint(x,y ),qs_max); - */ - } - painter->setPen(Qt::gray); - QRectF contentsRect = this->boundingRect(); - //painter->drawRect(0, 0, contentsRect.width()+1, contentsRect.height()+1); if (m_PiecewiseFunction && this->isEnabled()) { double* dp = m_PiecewiseFunction->GetDataPointer(); // Render lines painter->setPen(Qt::black); for (int i = -1; i < m_PiecewiseFunction->GetSize(); i++) { std::pair left; std::pair right; if(i < 0) left = this->FunctionToCanvas(std::make_pair(-32768, dp[0 * 2 + 1])); else left = this->FunctionToCanvas(std::make_pair(dp[i * 2], dp[i * 2 + 1])); if(i+1 >= m_PiecewiseFunction->GetSize()) right = this->FunctionToCanvas(std::make_pair(32768, dp[(i ) * 2 + 1])); else right = this->FunctionToCanvas(std::make_pair(dp[(i+1) * 2], dp[(i+1) * 2 + 1])); painter->drawLine(left.first, left.second, right.first, right.second); } // Render Points for (int i = 0; i < m_PiecewiseFunction->GetSize(); i++) { std::pair point = this->FunctionToCanvas(std::make_pair( dp[i * 2], dp[i * 2 + 1])); if (i == m_GrabbedHandle) { painter->setBrush(QBrush(Qt::red)); this->m_Opacity = QString::number(GetFunctionY(m_GrabbedHandle), 'g', 4).toFloat(); this->m_GreyValue = QString::number(GetFunctionX(m_GrabbedHandle), 'g', 4).toFloat(); emit this->sync(); } else { painter->setBrush(QBrush(Qt::green)); } painter->drawEllipse(point.first - 4, point.second - 4, 8, 8); } painter->setBrush(Qt::NoBrush); } } int QmlMitkPiecewiseFunctionCanvas::GetNearHandle(int x, int y, unsigned int maxSquaredDistance) { double* dp = m_PiecewiseFunction->GetDataPointer(); for (int i = 0; i < m_PiecewiseFunction->GetSize(); i++) { std::pair point = this->FunctionToCanvas(std::make_pair(dp[i * 2], dp[i * 2 + 1])); if ((unsigned int) ((point.first - x) * (point.first - x) + (point.second - y) * (point.second - y)) <= maxSquaredDistance) { return i; } } return -1; } void QmlMitkPiecewiseFunctionCanvas::MoveFunctionPoint(int index, std::pair pos) { RemoveFunctionPoint(GetFunctionX(index)); m_GrabbedHandle = AddFunctionPoint(pos.first, pos.second); } void QmlMitkPiecewiseFunctionCanvas::mousePressEvent( QMouseEvent* mouseEvent ) { QmlMitkTransferFunctionCanvas::mousePressEvent(mouseEvent); } void QmlMitkPiecewiseFunctionCanvas::mouseMoveEvent( QMouseEvent* mouseEvent ) { QmlMitkTransferFunctionCanvas::mouseMoveEvent(mouseEvent); } void QmlMitkPiecewiseFunctionCanvas::mouseReleaseEvent( QMouseEvent* mouseEvent ) { QmlMitkTransferFunctionCanvas::mouseReleaseEvent(mouseEvent); } void QmlMitkPiecewiseFunctionCanvas::mouseDoubleClickEvent( QMouseEvent* mouseEvent ) { QmlMitkTransferFunctionCanvas::mouseDoubleClickEvent(mouseEvent); } void QmlMitkPiecewiseFunctionCanvas::keyPressEvent(QKeyEvent *keyEvent) { QmlMitkTransferFunctionCanvas::keyPressEvent(keyEvent); } void QmlMitkPiecewiseFunctionCanvas::create() { qmlRegisterType("Mitk.Views", 1, 0, "PiecewiseItem"); } diff --git a/Modules/QmlItems/src/QmlMitkSliderLevelWindowItem.cpp b/Modules/QmlItems/src/QmlMitkSliderLevelWindowItem.cpp index d4a58ce026..35b715a169 100644 --- a/Modules/QmlItems/src/QmlMitkSliderLevelWindowItem.cpp +++ b/Modules/QmlItems/src/QmlMitkSliderLevelWindowItem.cpp @@ -1,571 +1,571 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include QmlMitkSliderLevelWindowItem* QmlMitkSliderLevelWindowItem::instance = nullptr; mitk::DataStorage::Pointer QmlMitkSliderLevelWindowItem::storage = nullptr; QmlMitkSliderLevelWindowItem::QmlMitkSliderLevelWindowItem( QQuickPaintedItem * parent ) : QQuickPaintedItem(parent) { m_Manager = mitk::LevelWindowManager::New(); m_Manager->SetDataStorage(QmlMitkSliderLevelWindowItem::storage); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmlMitkSliderLevelWindowItem::OnPropertyModified); m_ObserverTag = m_Manager->AddObserver(itk::ModifiedEvent(), command); m_IsObserverTagSet = true; setAcceptedMouseButtons(Qt::AllButtons); setAcceptHoverEvents(true); setAntialiasing(true); m_Resize = false; m_Bottom = false; m_CtrlPressed = false; m_MouseDown = false; m_ScaleVisible = true; this->setEnabled(false); update(); } QmlMitkSliderLevelWindowItem::~QmlMitkSliderLevelWindowItem() { if ( m_IsObserverTagSet) { m_Manager->RemoveObserver(m_ObserverTag); m_IsObserverTagSet = false; } } void QmlMitkSliderLevelWindowItem::setLevelWindowManager(mitk::LevelWindowManager* levelWindowManager) { if ( m_IsObserverTagSet) { m_Manager->RemoveObserver(m_ObserverTag); m_IsObserverTagSet = false; } m_Manager = levelWindowManager; if ( m_Manager.IsNotNull() ) { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmlMitkSliderLevelWindowItem::OnPropertyModified); m_ObserverTag = m_Manager->AddObserver(itk::ModifiedEvent(), command); m_IsObserverTagSet = true; } } void QmlMitkSliderLevelWindowItem::OnPropertyModified(const itk::EventObject& ) { try { m_LevelWindow = m_Manager->GetLevelWindow(); this->m_Level = (int)m_LevelWindow.GetLevel(); this->m_Window = (int)m_LevelWindow.GetWindow(); this->setEnabled(true); emit this->sync(); update(); } catch(...) { this->setEnabled(false); } QQuickPaintedItem::update(); } bool QmlMitkSliderLevelWindowItem::isEnabled() { return this->m_Enabled; } void QmlMitkSliderLevelWindowItem::setEnabled(bool enable) { this->m_Enabled = enable; emit this->enabledChanged(); } int QmlMitkSliderLevelWindowItem::fontSize() const { return this->m_FontSize; } void QmlMitkSliderLevelWindowItem::setFontSize(const int &fontSize) { this->m_FontSize = fontSize; this->m_Font.setPointSize(fontSize); } QColor QmlMitkSliderLevelWindowItem::fontColor() const { return this->m_FontColor; } void QmlMitkSliderLevelWindowItem::setFontColor(const QColor &color) { this->m_FontColor = color; } QColor QmlMitkSliderLevelWindowItem::color() const { return this->m_Color; } void QmlMitkSliderLevelWindowItem::setColor(const QColor &color) { this->m_Color = color; } QColor QmlMitkSliderLevelWindowItem::borderColor() const { return this->m_BorderColor; } void QmlMitkSliderLevelWindowItem::setBorderColor(const QColor &color) { this->m_BorderColor = color; } void QmlMitkSliderLevelWindowItem::setLevel(int level) { if(level != m_LevelWindow.GetLevel()) { m_LevelWindow.SetLevelWindow(level, m_LevelWindow.GetWindow()); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } this->m_Level = level; } int QmlMitkSliderLevelWindowItem::getLevel() { return this->m_Level; } void QmlMitkSliderLevelWindowItem::setWindow(int window) { if(window != m_LevelWindow.GetWindow()) { m_LevelWindow.SetLevelWindow(m_LevelWindow.GetLevel(), window); m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } this->m_Window = window; } int QmlMitkSliderLevelWindowItem::getWindow() { return this->m_Window; } void QmlMitkSliderLevelWindowItem::paint( QPainter* painter ) { if(!this->m_Enabled) return; m_MoveHeight = boundingRect().height() - 55; painter->setFont( m_Font ); painter->setPen(this->m_BorderColor); painter->setBrush(this->m_Color); painter->drawRoundedRect(m_Rect, 3, 3); painter->setPen(this->m_FontColor); float mr = m_LevelWindow.GetRange(); if ( mr < 1 ) mr = 1; float fact = (float) m_MoveHeight / mr; //begin draw scale if (m_ScaleVisible) { int minRange = (int)m_LevelWindow.GetRangeMin(); int maxRange = (int)m_LevelWindow.GetRangeMax(); int yValue = m_MoveHeight + (int)(minRange*fact); QString s = " 0"; if (minRange <= 0 && maxRange >= 0) { painter->drawLine( 5, yValue , 15, yValue); painter->drawText( 21, yValue + 3, s ); } int count = 1; int k = 5; bool enoughSpace = false; bool enoughSpace2 = false; double dStepSize = pow(10,floor(log10(mr/100))+1); for(int i = m_MoveHeight + (int)(minRange*fact); i < m_MoveHeight;)//negative { if (-count*dStepSize < minRange) break; yValue = m_MoveHeight + (int)((minRange + count*dStepSize)*fact); s = QString::number(-count*dStepSize); if (count % k && ((dStepSize*fact) > 2.5)) { painter->drawLine( 8, yValue, 12, yValue); enoughSpace = true; } else if (!(count % k)) { if ((k*dStepSize*fact) > 7) { painter->drawLine( 5, yValue, 15, yValue); painter->drawText( 21, yValue + 3, s ); enoughSpace2 = true; } else { k += 5; } } if (enoughSpace) { i=yValue; count++; } else if (enoughSpace2) { i=yValue; count += k; } else { i=yValue; count = k; } } count = 1; k = 5; enoughSpace = false; enoughSpace2 = false; for(int i = m_MoveHeight + (int)(minRange*fact); i >= 0;) { if (count*dStepSize > maxRange) break; yValue = m_MoveHeight + (int)((minRange - count*dStepSize)*fact); s = QString::number(count*dStepSize); if(count % k && ((dStepSize*fact) > 2.5)) { if (!(minRange > 0 && (count*dStepSize) < minRange)) painter->drawLine( 8, yValue, 12, yValue); enoughSpace = true; } else if (!(count % k)) { if ((k*dStepSize*fact) > 7) { if (!(minRange > 0 && (count*dStepSize) < minRange)) { painter->drawLine( 5, yValue, 15, yValue); painter->drawText( 21, yValue + 3, s ); } enoughSpace2 = true; } else { k += 5; } } if (enoughSpace) { i=yValue; count++; } else if (enoughSpace2) { i=yValue; count += k; } else { i=yValue; count = k; } } } } void QmlMitkSliderLevelWindowItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry) { update(); QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry); } void QmlMitkSliderLevelWindowItem::hoverMoveEvent(QHoverEvent *mouseEvent) { if ( mouseEvent->pos().y() >= 0 && mouseEvent->pos().y() <= (m_Rect.topLeft().y() + 3) ) { setCursor(Qt::SizeVerCursor); m_UpperBound.setRect(m_Rect.topLeft().x(), m_Rect.topLeft().y() - 3, 17, 7); //this->setToolTip("Ctrl + left click to change only upper bound"); m_Resize = true; } else if ( mouseEvent->pos().y() >= (m_Rect.bottomLeft().y() - 3) ) { setCursor(Qt::SizeVerCursor); m_LowerBound.setRect(m_Rect.bottomLeft().x(), m_Rect.bottomLeft().y() - 3, 17, 7); //this->setToolTip("Ctrl + left click to change only lower bound"); m_Resize = true; m_Bottom = true; } else { setCursor(Qt::ArrowCursor); //this->setToolTip("Left click and mouse move to adjust the slider"); m_Resize = false; m_Bottom = false; } } /** * */ void QmlMitkSliderLevelWindowItem::mouseMoveEvent( QMouseEvent* mouseEvent ) { if(!mouseEvent && !m_MouseDown) return; if ( m_LevelWindow.IsFixed() ) return; float fact = (float) m_MoveHeight / m_LevelWindow.GetRange(); if ( m_Leftbutton ) { if (m_Resize && !m_CtrlPressed) { double diff = (mouseEvent->pos().y()) / fact; diff -= (m_StartPos.y()) / fact; m_StartPos = mouseEvent->pos(); if (diff == 0) return; float value; if (m_Bottom) value = m_LevelWindow.GetWindow() + ( ( 2 * diff ) ); else value = m_LevelWindow.GetWindow() - ( ( 2 * diff ) ); if ( value < 0 ) value = 0; m_LevelWindow.SetLevelWindow( m_LevelWindow.GetLevel(), value ); } else if(m_Resize && m_CtrlPressed) { if (!m_Bottom) { double diff = (mouseEvent->pos().y()) / fact; diff -= (m_StartPos.y()) / fact; m_StartPos = mouseEvent->pos(); if (diff == 0) return; float value; value = m_LevelWindow.GetWindow() - ( ( diff ) ); if ( value < 0 ) value = 0; float oldWindow; float oldLevel; float newLevel; oldWindow = m_LevelWindow.GetWindow(); oldLevel = m_LevelWindow.GetLevel(); newLevel = oldLevel + (value - oldWindow)/2; if (!((newLevel + value/2) > m_LevelWindow.GetRangeMax())) m_LevelWindow.SetLevelWindow( newLevel, value ); } else { double diff = (mouseEvent->pos().y()) / fact; diff -= (m_StartPos.y()) / fact; m_StartPos = mouseEvent->pos(); if (diff == 0) return; float value; value = m_LevelWindow.GetWindow() + ( ( diff ) ); if ( value < 0 ) value = 0; float oldWindow; float oldLevel; float newLevel; oldWindow = m_LevelWindow.GetWindow(); oldLevel = m_LevelWindow.GetLevel(); newLevel = oldLevel - (value - oldWindow)/2; if (!((newLevel - value/2) < m_LevelWindow.GetRangeMin())) m_LevelWindow.SetLevelWindow( newLevel, value ); } } else { const float minv = m_LevelWindow.GetRangeMin(); const float level = (m_MoveHeight - mouseEvent->pos().y()) / fact + minv; double diff = (mouseEvent->pos().x()) / fact; diff -= (m_StartPos.x()) / fact; m_StartPos = mouseEvent->pos(); float window; if (m_Bottom) window = m_LevelWindow.GetWindow() + ( ( 2 * diff ) ); else window = m_LevelWindow.GetWindow() - ( ( 2 * diff ) ); if ( window < 0 ) window = 0; m_LevelWindow.SetLevelWindow( level, window ); } m_Manager->SetLevelWindow(m_LevelWindow); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } /** * */ void QmlMitkSliderLevelWindowItem::mousePressEvent( QMouseEvent* mouseEvent ) { if ( m_LevelWindow.IsFixed() ) return; m_MouseDown = true; m_StartPos = mouseEvent->pos(); if ( mouseEvent->button() == Qt::LeftButton ) { if (mouseEvent->modifiers() == Qt::ControlModifier || mouseEvent->modifiers() == Qt::ShiftModifier) { m_CtrlPressed = true; } else { m_CtrlPressed = false; } m_Leftbutton = true; } else m_Leftbutton = false; mouseMoveEvent( mouseEvent ); } /** * */ void QmlMitkSliderLevelWindowItem::mouseReleaseEvent( QMouseEvent* ) { if ( m_LevelWindow.IsFixed() ) return; m_MouseDown = false; } /** * */ void QmlMitkSliderLevelWindowItem::update() { int rectWidth; if(m_ScaleVisible) { rectWidth = 16; } else { rectWidth = 26; } float mr = m_LevelWindow.GetRange(); if ( mr < 1 ) mr = 1; float fact = (float) m_MoveHeight / mr; float rectHeight = m_LevelWindow.GetWindow() * fact; if ( rectHeight < 15 ) rectHeight = 15; if ( m_LevelWindow.GetLowerWindowBound() < 0 ) m_Rect.setRect( 2, (int) (m_MoveHeight - (m_LevelWindow.GetUpperWindowBound() - m_LevelWindow.GetRangeMin()) * fact) , rectWidth, (int) rectHeight ); else m_Rect.setRect( 2, (int) (m_MoveHeight - (m_LevelWindow.GetUpperWindowBound() - m_LevelWindow.GetRangeMin()) * fact), rectWidth, (int) rectHeight ); QQuickPaintedItem::update(); } void QmlMitkSliderLevelWindowItem::hideScale() { m_ScaleVisible = false; update(); } void QmlMitkSliderLevelWindowItem::showScale() { m_ScaleVisible = true; update(); } void QmlMitkSliderLevelWindowItem::setDataStorage(mitk::DataStorage* ds) { m_Manager->SetDataStorage(ds); } mitk::LevelWindowManager* QmlMitkSliderLevelWindowItem::GetManager() { return m_Manager.GetPointer(); } -void QmlMitkSliderLevelWindowItem::create(QQmlEngine &engine, mitk::DataStorage::Pointer storage) +void QmlMitkSliderLevelWindowItem::create(QQmlEngine &, mitk::DataStorage::Pointer storage) { qmlRegisterType("Mitk.Views", 1, 0, "LevelWindow"); QmlMitkSliderLevelWindowItem::storage = storage; } diff --git a/Modules/QtWidgets/src/QmitkDataStorageListModel.cpp b/Modules/QtWidgets/src/QmitkDataStorageListModel.cpp index 2a0b5248d5..93aeeaa827 100755 --- a/Modules/QtWidgets/src/QmitkDataStorageListModel.cpp +++ b/Modules/QtWidgets/src/QmitkDataStorageListModel.cpp @@ -1,334 +1,333 @@ /*=================================================================== 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 "QmitkDataStorageListModel.h" //# Own includes // mitk #include "mitkStringProperty.h" //# Toolkit includes // itk #include "itkCommand.h" QmitkDataStorageListModel::QmitkDataStorageListModel(mitk::DataStorage *dataStorage, mitk::NodePredicateBase::Pointer pred, QObject *parent) : QAbstractListModel(parent), m_NodePredicate(nullptr), m_DataStorage(nullptr), m_BlockEvents(false) { this->SetPredicate(pred); this->SetDataStorage(dataStorage); } QmitkDataStorageListModel::~QmitkDataStorageListModel() { // set data storage to nullptr so that the event listener gets removed this->SetDataStorage(nullptr); } void QmitkDataStorageListModel::SetDataStorage(mitk::DataStorage::Pointer dataStorage) { if (m_DataStorage == dataStorage) { return; } // remove old listeners if (m_DataStorage != nullptr) { m_DataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageListModel::OnDataStorageNodeAdded)); m_DataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1( this, &QmitkDataStorageListModel::OnDataStorageNodeRemoved)); m_DataStorage->RemoveObserver(m_DataStorageDeleteObserverTag); m_DataStorageDeleteObserverTag = 0; } m_DataStorage = dataStorage; if (m_DataStorage != nullptr) { // subscribe for node added/removed events m_DataStorage->AddNodeEvent.AddListener(mitk::MessageDelegate1( this, &QmitkDataStorageListModel::OnDataStorageNodeAdded)); m_DataStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1( this, &QmitkDataStorageListModel::OnDataStorageNodeRemoved)); // add ITK delete listener on data storage itk::MemberCommand::Pointer deleteCommand = itk::MemberCommand::New(); deleteCommand->SetCallbackFunction(this, &QmitkDataStorageListModel::OnDataStorageDeleted); m_DataStorageDeleteObserverTag = m_DataStorage->AddObserver(itk::DeleteEvent(), deleteCommand); } // reset/rebuild model reset(); } Qt::ItemFlags QmitkDataStorageListModel::flags(const QModelIndex &) const { return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant QmitkDataStorageListModel::data(const QModelIndex &index, int role) const { if (role == Qt::DisplayRole && index.isValid()) { const mitk::DataNode *node = std::get(m_NodesAndObserverTags.at(index.row())); return QVariant(QString::fromStdString(node->GetName())); } else { return QVariant(); } } QVariant QmitkDataStorageListModel::headerData(int /*section*/, Qt::Orientation /*orientation*/, int /*role*/) const { return QVariant(tr("Nodes")); } int QmitkDataStorageListModel::rowCount(const QModelIndex & /*parent*/) const { return m_NodesAndObserverTags.size(); } std::vector QmitkDataStorageListModel::GetDataNodes() const { auto size = m_NodesAndObserverTags.size(); std::vector result(size); - for (int i = 0; i < size; ++i) + for (std::size_t i = 0; i < size; ++i) { result[i] = std::get(m_NodesAndObserverTags[i]); } return result; } mitk::DataStorage *QmitkDataStorageListModel::GetDataStorage() const { return m_DataStorage; } void QmitkDataStorageListModel::SetPredicate(mitk::NodePredicateBase *pred) { m_NodePredicate = pred; // in a prior implementation the call to beginResetModel() been after reset(). // Should this actually be the better order of calls, please document! QAbstractListModel::beginResetModel(); reset(); QAbstractListModel::endResetModel(); } mitk::NodePredicateBase *QmitkDataStorageListModel::GetPredicate() const { return m_NodePredicate; } void QmitkDataStorageListModel::reset() { mitk::DataStorage::SetOfObjects::ConstPointer modelNodes; if (m_DataStorage != nullptr) { if (m_NodePredicate != nullptr) { modelNodes = m_DataStorage->GetSubset(m_NodePredicate); } else { modelNodes = m_DataStorage->GetAll(); } } ClearInternalNodeList(); // add all filtered nodes to our list if (modelNodes != nullptr) { for (auto &node : *modelNodes) { AddNodeToInternalList(node); } } } void QmitkDataStorageListModel::AddNodeToInternalList(mitk::DataNode *node) { if (m_DataStorage != nullptr) { itk::MemberCommand::Pointer nodeModifiedCommand; // add modified observer nodeModifiedCommand = itk::MemberCommand::New(); nodeModifiedCommand->SetCallbackFunction(this, &QmitkDataStorageListModel::OnDataNodeModified); unsigned long nodeObserverTag = node->AddObserver(itk::ModifiedEvent(), nodeModifiedCommand); itk::MemberCommand::Pointer dataModifiedCommand; unsigned long dataObserverTag = 0; // add modified observer if (node->GetData() != nullptr) { dataModifiedCommand = itk::MemberCommand::New(); dataModifiedCommand->SetCallbackFunction(this, &QmitkDataStorageListModel::OnDataModified); dataObserverTag = node->GetData()->AddObserver(itk::ModifiedEvent(), dataModifiedCommand); } m_NodesAndObserverTags.push_back(std::make_tuple(node, nodeObserverTag, dataObserverTag)); } } void QmitkDataStorageListModel::ClearInternalNodeList() { for (auto &iter : m_NodesAndObserverTags) { auto node = std::get(iter); if (node != nullptr) { node->RemoveObserver(std::get(iter)); if (node->GetData() != nullptr) { node->GetData()->RemoveObserver(std::get(iter)); } } } m_NodesAndObserverTags.clear(); } void QmitkDataStorageListModel::RemoveNodeFromInternalList(mitk::DataNode *node) { for (auto iter = m_NodesAndObserverTags.begin(); iter != m_NodesAndObserverTags.end(); ++iter) { if (std::get(*iter) == node) { node->RemoveObserver(std::get(*iter)); if (node->GetData() != nullptr) { node->GetData()->RemoveObserver(std::get(*iter)); } m_NodesAndObserverTags.erase(iter); // invalidate iter break; } } } void QmitkDataStorageListModel::OnDataStorageNodeAdded(const mitk::DataNode *node) { // guarantee no recursions when a new node event is thrown if (!m_BlockEvents) { m_BlockEvents = true; // check if node should be added to the model bool addNode = true; if (m_NodePredicate && !m_NodePredicate->CheckNode(node)) addNode = false; if (addNode) { int newIndex = m_NodesAndObserverTags.size(); beginInsertRows(QModelIndex(), newIndex, newIndex); AddNodeToInternalList(const_cast(node)); endInsertRows(); } m_BlockEvents = false; } } void QmitkDataStorageListModel::OnDataStorageNodeRemoved(const mitk::DataNode *node) { // guarantee no recursions when a new node event is thrown if (!m_BlockEvents) { m_BlockEvents = true; int row = 0; for (auto iter = m_NodesAndObserverTags.begin(); iter != m_NodesAndObserverTags.end(); ++iter, ++row) { if (std::get(*iter) == node) { // node found, remove it beginRemoveRows(QModelIndex(), row, row); RemoveNodeFromInternalList(std::get(*iter)); endRemoveRows(); break; } } } m_BlockEvents = false; } void QmitkDataStorageListModel::OnDataNodeModified(const itk::Object *caller, const itk::EventObject & /*event*/) { if (m_BlockEvents) return; const mitk::DataNode *modifiedNode = dynamic_cast(caller); if (modifiedNode) { QModelIndex changedIndex = getIndex(modifiedNode); if (changedIndex.isValid()) { emit dataChanged(changedIndex, changedIndex); } } } void QmitkDataStorageListModel::OnDataModified(const itk::Object *caller, const itk::EventObject &event) { OnDataNodeModified(caller, event); // until different implementation } -void QmitkDataStorageListModel::OnDataStorageDeleted(const itk::Object *caller, const itk::EventObject & /*event*/) +void QmitkDataStorageListModel::OnDataStorageDeleted(const itk::Object *, const itk::EventObject &) { if (m_BlockEvents) return; - // set data storage to nullptr -> empty model this->SetDataStorage(nullptr); } mitk::DataNode::Pointer QmitkDataStorageListModel::getNode(const QModelIndex &index) const { if (index.isValid()) { return std::get(m_NodesAndObserverTags.at(index.row())); } else { return nullptr; } } QModelIndex QmitkDataStorageListModel::getIndex(const mitk::DataNode *node) const { int row = 0; for (auto iter = m_NodesAndObserverTags.begin(); iter != m_NodesAndObserverTags.end(); ++iter, ++row) { if (std::get(*iter) == node) { return index(row); } } return QModelIndex(); } diff --git a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp index 0920501edb..03b998e07f 100644 --- a/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp +++ b/Modules/QtWidgets/src/QmitkStdMultiWidget.cpp @@ -1,2021 +1,2021 @@ /*=================================================================== 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 SMW_INFO MITK_INFO("widget.stdmulti") #include "QmitkStdMultiWidget.h" #include #include #include #include #include #include #include #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include QmitkStdMultiWidget::QmitkStdMultiWidget(QWidget *parent, Qt::WindowFlags f, mitk::RenderingManager *renderingManager, mitk::BaseRenderer::RenderingMode::Type renderingMode, const QString &name) : QWidget(parent, f), mitkWidget1(nullptr), mitkWidget2(nullptr), mitkWidget3(nullptr), mitkWidget4(nullptr), levelWindowWidget(nullptr), QmitkStdMultiWidgetLayout(nullptr), m_Layout(LAYOUT_DEFAULT), m_PlaneMode(PLANE_MODE_SLICING), m_RenderingManager(renderingManager), m_GradientBackgroundFlag(true), m_TimeNavigationController(nullptr), m_MainSplit(nullptr), m_LayoutSplit(nullptr), m_SubSplit1(nullptr), m_SubSplit2(nullptr), mitkWidget1Container(nullptr), mitkWidget2Container(nullptr), mitkWidget3Container(nullptr), mitkWidget4Container(nullptr), m_PendingCrosshairPositionEvent(false), m_CrosshairNavigationEnabled(false) { /****************************************************** * Use the global RenderingManager if none was specified * ****************************************************/ if (m_RenderingManager == nullptr) { m_RenderingManager = mitk::RenderingManager::GetInstance(); } m_TimeNavigationController = m_RenderingManager->GetTimeNavigationController(); /*******************************/ // Create Widget manually /*******************************/ // create Layouts QmitkStdMultiWidgetLayout = new QHBoxLayout(this); QmitkStdMultiWidgetLayout->setContentsMargins(0, 0, 0, 0); // Set Layout to widget this->setLayout(QmitkStdMultiWidgetLayout); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // creae Widget Container mitkWidget1Container = new QWidget(m_SubSplit1); mitkWidget2Container = new QWidget(m_SubSplit1); mitkWidget3Container = new QWidget(m_SubSplit2); mitkWidget4Container = new QWidget(m_SubSplit2); mitkWidget1Container->setContentsMargins(0, 0, 0, 0); mitkWidget2Container->setContentsMargins(0, 0, 0, 0); mitkWidget3Container->setContentsMargins(0, 0, 0, 0); mitkWidget4Container->setContentsMargins(0, 0, 0, 0); // create Widget Layout QHBoxLayout *mitkWidgetLayout1 = new QHBoxLayout(mitkWidget1Container); QHBoxLayout *mitkWidgetLayout2 = new QHBoxLayout(mitkWidget2Container); QHBoxLayout *mitkWidgetLayout3 = new QHBoxLayout(mitkWidget3Container); QHBoxLayout *mitkWidgetLayout4 = new QHBoxLayout(mitkWidget4Container); m_CornerAnnotations[0] = vtkSmartPointer::New(); m_CornerAnnotations[1] = vtkSmartPointer::New(); m_CornerAnnotations[2] = vtkSmartPointer::New(); m_CornerAnnotations[3] = vtkSmartPointer::New(); m_RectangleProps[0] = vtkSmartPointer::New(); m_RectangleProps[1] = vtkSmartPointer::New(); m_RectangleProps[2] = vtkSmartPointer::New(); m_RectangleProps[3] = vtkSmartPointer::New(); mitkWidgetLayout1->setMargin(0); mitkWidgetLayout2->setMargin(0); mitkWidgetLayout3->setMargin(0); mitkWidgetLayout4->setMargin(0); // set Layout to Widget Container mitkWidget1Container->setLayout(mitkWidgetLayout1); mitkWidget2Container->setLayout(mitkWidgetLayout2); mitkWidget3Container->setLayout(mitkWidgetLayout3); mitkWidget4Container->setLayout(mitkWidgetLayout4); // set SizePolicy mitkWidget1Container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mitkWidget2Container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mitkWidget3Container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); mitkWidget4Container->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // insert Widget Container into the splitters m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit2->addWidget(mitkWidget3Container); m_SubSplit2->addWidget(mitkWidget4Container); // Create RenderWindows 1 mitkWidget1 = new QmitkRenderWindow(mitkWidget1Container, name + ".widget1", nullptr, m_RenderingManager, renderingMode); mitkWidget1->SetLayoutIndex(AXIAL); mitkWidgetLayout1->addWidget(mitkWidget1); // Create RenderWindows 2 mitkWidget2 = new QmitkRenderWindow(mitkWidget2Container, name + ".widget2", nullptr, m_RenderingManager, renderingMode); mitkWidget2->setEnabled(true); mitkWidget2->SetLayoutIndex(SAGITTAL); mitkWidgetLayout2->addWidget(mitkWidget2); // Create RenderWindows 3 mitkWidget3 = new QmitkRenderWindow(mitkWidget3Container, name + ".widget3", nullptr, m_RenderingManager, renderingMode); mitkWidget3->SetLayoutIndex(CORONAL); mitkWidgetLayout3->addWidget(mitkWidget3); // Create RenderWindows 4 mitkWidget4 = new QmitkRenderWindow(mitkWidget4Container, name + ".widget4", nullptr, m_RenderingManager, renderingMode); mitkWidget4->SetLayoutIndex(THREE_D); mitkWidgetLayout4->addWidget(mitkWidget4); // create SignalSlot Connection connect(mitkWidget1, SIGNAL(SignalLayoutDesignChanged(int)), this, SLOT(OnLayoutDesignChanged(int))); connect(mitkWidget1, SIGNAL(ResetView()), this, SLOT(ResetCrosshair())); connect(mitkWidget1, SIGNAL(ChangeCrosshairRotationMode(int)), this, SLOT(SetWidgetPlaneMode(int))); connect(this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget1, SLOT(OnWidgetPlaneModeChanged(int))); connect(mitkWidget2, SIGNAL(SignalLayoutDesignChanged(int)), this, SLOT(OnLayoutDesignChanged(int))); connect(mitkWidget2, SIGNAL(ResetView()), this, SLOT(ResetCrosshair())); connect(mitkWidget2, SIGNAL(ChangeCrosshairRotationMode(int)), this, SLOT(SetWidgetPlaneMode(int))); connect(this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget2, SLOT(OnWidgetPlaneModeChanged(int))); connect(mitkWidget3, SIGNAL(SignalLayoutDesignChanged(int)), this, SLOT(OnLayoutDesignChanged(int))); connect(mitkWidget3, SIGNAL(ResetView()), this, SLOT(ResetCrosshair())); connect(mitkWidget3, SIGNAL(ChangeCrosshairRotationMode(int)), this, SLOT(SetWidgetPlaneMode(int))); connect(this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget3, SLOT(OnWidgetPlaneModeChanged(int))); connect(mitkWidget4, SIGNAL(SignalLayoutDesignChanged(int)), this, SLOT(OnLayoutDesignChanged(int))); connect(mitkWidget4, SIGNAL(ResetView()), this, SLOT(ResetCrosshair())); connect(mitkWidget4, SIGNAL(ChangeCrosshairRotationMode(int)), this, SLOT(SetWidgetPlaneMode(int))); connect(this, SIGNAL(WidgetNotifyNewCrossHairMode(int)), mitkWidget4, SLOT(OnWidgetPlaneModeChanged(int))); // Create Level Window Widget levelWindowWidget = new QmitkLevelWindowWidget(m_MainSplit); // this levelWindowWidget->setObjectName(QString::fromUtf8("levelWindowWidget")); QSizePolicy sizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred); sizePolicy.setHorizontalStretch(0); sizePolicy.setVerticalStretch(0); sizePolicy.setHeightForWidth(levelWindowWidget->sizePolicy().hasHeightForWidth()); levelWindowWidget->setSizePolicy(sizePolicy); levelWindowWidget->setMaximumWidth(50); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // resize Image. this->resize(QSize(364, 477).expandedTo(minimumSizeHint())); // Initialize the widgets. this->InitializeWidget(); // Activate Widget Menu this->ActivateMenuWidget(true); } void QmitkStdMultiWidget::InitializeWidget() { // Make all black and overwrite renderwindow 4 this->FillGradientBackgroundWithBlack(); // This is #191919 in hex float tmp1[3] = {0.098f, 0.098f, 0.098f}; // This is #7F7F7F in hex float tmp2[3] = {0.498f, 0.498f, 0.498f}; m_GradientBackgroundColors[3] = std::make_pair(mitk::Color(tmp1), mitk::Color(tmp2)); // Yellow is default color for widget4 m_DecorationColorWidget4[0] = 1.0f; m_DecorationColorWidget4[1] = 1.0f; m_DecorationColorWidget4[2] = 0.0f; // transfer colors in WorldGeometry-Nodes of the associated Renderer mitk::IntProperty::Pointer layer; // of widget 1 m_PlaneNode1 = mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetColor(GetDecorationColor(0)); layer = mitk::IntProperty::New(1000); m_PlaneNode1->SetProperty("layer", layer); // ... of widget 2 m_PlaneNode2 = mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetColor(GetDecorationColor(1)); layer = mitk::IntProperty::New(1000); m_PlaneNode2->SetProperty("layer", layer); // ... of widget 3 m_PlaneNode3 = mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetColor(GetDecorationColor(2)); layer = mitk::IntProperty::New(1000); m_PlaneNode3->SetProperty("layer", layer); // The parent node m_ParentNodeForGeometryPlanes = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->GetCurrentWorldPlaneGeometryNode(); layer = mitk::IntProperty::New(1000); m_ParentNodeForGeometryPlanes->SetProperty("layer", layer); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->SetMapperID(mitk::BaseRenderer::Standard3D); // Set plane mode (slicing/rotation behavior) to slicing (default) m_PlaneMode = PLANE_MODE_SLICING; // Set default view directions for SNCs mitkWidget1->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Axial); mitkWidget2->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Sagittal); mitkWidget3->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Frontal); mitkWidget4->GetSliceNavigationController()->SetDefaultViewDirection(mitk::SliceNavigationController::Original); SetDecorationProperties("Axial", GetDecorationColor(0), 0); SetDecorationProperties("Sagittal", GetDecorationColor(1), 1); SetDecorationProperties("Coronal", GetDecorationColor(2), 2); SetDecorationProperties("3D", GetDecorationColor(3), 3); // connect to the "time navigation controller": send time via sliceNavigationControllers m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget1->GetSliceNavigationController(), false); m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget2->GetSliceNavigationController(), false); m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget3->GetSliceNavigationController(), false); m_TimeNavigationController->ConnectGeometryTimeEvent(mitkWidget4->GetSliceNavigationController(), false); mitkWidget1->GetSliceNavigationController()->ConnectGeometrySendEvent( mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())); // reverse connection between sliceNavigationControllers and m_TimeNavigationController mitkWidget1->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); mitkWidget2->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); mitkWidget3->GetSliceNavigationController()->ConnectGeometryTimeEvent(m_TimeNavigationController, false); // mitkWidget4->GetSliceNavigationController() // ->ConnectGeometryTimeEvent(m_TimeNavigationController, false); m_MouseModeSwitcher = mitk::MouseModeSwitcher::New(); // setup the department logo rendering m_LogoRendering = mitk::LogoAnnotation::New(); mitk::BaseRenderer::Pointer renderer4 = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow()); m_LogoRendering->SetOpacity(0.5); mitk::Point2D offset; offset.Fill(0.03); m_LogoRendering->SetOffsetVector(offset); m_LogoRendering->SetRelativeSize(0.15); m_LogoRendering->SetCornerPosition(1); m_LogoRendering->SetLogoImagePath("DefaultLogo"); mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_LogoRendering.GetPointer(), renderer4); } void QmitkStdMultiWidget::FillGradientBackgroundWithBlack() { // We have 4 widgets and ... for (unsigned int i = 0; i < 4; ++i) { float black[3] = {0.0f, 0.0f, 0.0f}; m_GradientBackgroundColors[i] = std::make_pair(mitk::Color(black), mitk::Color(black)); } } std::pair QmitkStdMultiWidget::GetGradientColors(unsigned int widgetNumber) { if (widgetNumber > 3) { MITK_ERROR << "Decoration color for unknown widget!"; float black[3] = {0.0f, 0.0f, 0.0f}; return std::make_pair(mitk::Color(black), mitk::Color(black)); } return m_GradientBackgroundColors[widgetNumber]; } mitk::Color QmitkStdMultiWidget::GetDecorationColor(unsigned int widgetNumber) { // The implementation looks a bit messy here, but it avoids // synchronization of the color of the geometry nodes and an // internal member here. // Default colors were chosen for decent visibitliy. // Feel free to change your preferences in the workbench. float tmp[3] = {0.0f, 0.0f, 0.0f}; switch (widgetNumber) { case 0: { if (m_PlaneNode1.IsNotNull()) { if (m_PlaneNode1->GetColor(tmp)) { return dynamic_cast(m_PlaneNode1->GetProperty("color"))->GetColor(); } } float red[3] = {0.753f, 0.0f, 0.0f}; // This is #C00000 in hex return mitk::Color(red); } case 1: { if (m_PlaneNode2.IsNotNull()) { if (m_PlaneNode2->GetColor(tmp)) { return dynamic_cast(m_PlaneNode2->GetProperty("color"))->GetColor(); } } float green[3] = {0.0f, 0.69f, 0.0f}; // This is #00B000 in hex return mitk::Color(green); } case 2: { if (m_PlaneNode3.IsNotNull()) { if (m_PlaneNode3->GetColor(tmp)) { return dynamic_cast(m_PlaneNode3->GetProperty("color"))->GetColor(); } } float blue[3] = {0.0, 0.502f, 1.0f}; // This is #0080FF in hex return mitk::Color(blue); } case 3: { return m_DecorationColorWidget4; } default: MITK_ERROR << "Decoration color for unknown widget!"; float black[3] = {0.0f, 0.0f, 0.0f}; return mitk::Color(black); } } std::string QmitkStdMultiWidget::GetCornerAnnotationText(unsigned int widgetNumber) { if (widgetNumber > 3) { MITK_ERROR << "Decoration color for unknown widget!"; return std::string(""); } return std::string(m_CornerAnnotations[widgetNumber]->GetText(0)); } QmitkStdMultiWidget::~QmitkStdMultiWidget() { DisablePositionTracking(); // DisableNavigationControllerEventListening(); m_TimeNavigationController->Disconnect(mitkWidget1->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(mitkWidget2->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(mitkWidget3->GetSliceNavigationController()); m_TimeNavigationController->Disconnect(mitkWidget4->GetSliceNavigationController()); } void QmitkStdMultiWidget::RemovePlanesFromDataStorage() { if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { if (m_DataStorage.IsNotNull()) { m_DataStorage->Remove(m_PlaneNode1); m_DataStorage->Remove(m_PlaneNode2); m_DataStorage->Remove(m_PlaneNode3); m_DataStorage->Remove(m_ParentNodeForGeometryPlanes); } } } void QmitkStdMultiWidget::AddPlanesToDataStorage() { if (m_PlaneNode1.IsNotNull() && m_PlaneNode2.IsNotNull() && m_PlaneNode3.IsNotNull() && m_ParentNodeForGeometryPlanes.IsNotNull()) { if (m_DataStorage.IsNotNull()) { m_DataStorage->Add(m_ParentNodeForGeometryPlanes); m_DataStorage->Add(m_PlaneNode1, m_ParentNodeForGeometryPlanes); m_DataStorage->Add(m_PlaneNode2, m_ParentNodeForGeometryPlanes); m_DataStorage->Add(m_PlaneNode3, m_ParentNodeForGeometryPlanes); } } } void QmitkStdMultiWidget::changeLayoutTo2DImagesUp() { SMW_INFO << "changing layout to 2D images up... " << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // Set Layout to widget this->setLayout(QmitkStdMultiWidgetLayout); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget Container into splitter top m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit1->addWidget(mitkWidget3Container); // set SplitterSize for splitter top QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); // insert Widget Container into splitter bottom m_SubSplit2->addWidget(mitkWidget4Container); // set SplitterSize for splitter m_LayoutSplit splitterSize.clear(); splitterSize.push_back(400); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt m_MainSplit->show(); // show Widget if hidden if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); // Change Layout Name m_Layout = LAYOUT_2D_IMAGES_UP; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2D_IMAGES_UP); mitkWidget2->LayoutDesignListChanged(LAYOUT_2D_IMAGES_UP); mitkWidget3->LayoutDesignListChanged(LAYOUT_2D_IMAGES_UP); mitkWidget4->LayoutDesignListChanged(LAYOUT_2D_IMAGES_UP); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutTo2DImagesLeft() { SMW_INFO << "changing layout to 2D images left... " << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(Qt::Vertical, m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget into the splitters m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit1->addWidget(mitkWidget3Container); // set splitterSize of SubSplit1 QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); m_SubSplit2->addWidget(mitkWidget4Container); // set splitterSize of Layout Split splitterSize.clear(); splitterSize.push_back(400); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show Widget if hidden if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); // update Layout Name m_Layout = LAYOUT_2D_IMAGES_LEFT; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2D_IMAGES_LEFT); mitkWidget2->LayoutDesignListChanged(LAYOUT_2D_IMAGES_LEFT); mitkWidget3->LayoutDesignListChanged(LAYOUT_2D_IMAGES_LEFT); mitkWidget4->LayoutDesignListChanged(LAYOUT_2D_IMAGES_LEFT); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::SetDecorationProperties(std::string text, mitk::Color color, int widgetNumber) { if (widgetNumber > 3) { MITK_ERROR << "Unknown render window for annotation."; return; } vtkRenderer *renderer = this->GetRenderWindow(widgetNumber)->GetRenderer()->GetVtkRenderer(); if (!renderer) return; vtkSmartPointer annotation = m_CornerAnnotations[widgetNumber]; annotation->SetText(0, text.c_str()); annotation->SetMaximumFontSize(12); annotation->GetTextProperty()->SetColor(color[0], color[1], color[2]); if (!renderer->HasViewProp(annotation)) { renderer->AddViewProp(annotation); } vtkSmartPointer frame = m_RectangleProps[widgetNumber]; frame->SetColor(color[0], color[1], color[2]); if (!renderer->HasViewProp(frame)) { renderer->AddViewProp(frame); } } void QmitkStdMultiWidget::SetCornerAnnotationVisibility(bool visibility) { for (int i = 0; i < 4; ++i) { m_CornerAnnotations[i]->SetVisibility(visibility); } } bool QmitkStdMultiWidget::IsCornerAnnotationVisible(void) const { return m_CornerAnnotations[0]->GetVisibility() > 0; } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow(unsigned int number) { switch (number) { case 0: return this->GetRenderWindow1(); case 1: return this->GetRenderWindow2(); case 2: return this->GetRenderWindow3(); case 3: return this->GetRenderWindow4(); default: MITK_ERROR << "Requested unknown render window"; break; } return nullptr; } void QmitkStdMultiWidget::changeLayoutToDefault() { SMW_INFO << "changing layout to default... " << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget container into the splitters m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit2->addWidget(mitkWidget3Container); m_SubSplit2->addWidget(mitkWidget4Container); // set splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); m_SubSplit2->setSizes(splitterSize); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show Widget if hidden if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_DEFAULT; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_DEFAULT); mitkWidget2->LayoutDesignListChanged(LAYOUT_DEFAULT); mitkWidget3->LayoutDesignListChanged(LAYOUT_DEFAULT); mitkWidget4->LayoutDesignListChanged(LAYOUT_DEFAULT); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToBig3D() { SMW_INFO << "changing layout to big 3D ..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // add widget Splitter to main Splitter m_MainSplit->addWidget(mitkWidget4Container); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); mitkWidget3->hide(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_BIG_3D; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_BIG_3D); mitkWidget2->LayoutDesignListChanged(LAYOUT_BIG_3D); mitkWidget3->LayoutDesignListChanged(LAYOUT_BIG_3D); mitkWidget4->LayoutDesignListChanged(LAYOUT_BIG_3D); // update Alle Widgets this->UpdateAllWidgets(); mitk::RenderingManager::GetInstance()->SetRenderWindowFocus(mitkWidget4->GetVtkRenderWindow()); } void QmitkStdMultiWidget::changeLayoutToWidget1() { SMW_INFO << "changing layout to big Widget1 ..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // add widget Splitter to main Splitter m_MainSplit->addWidget(mitkWidget1Container); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets if (mitkWidget1->isHidden()) mitkWidget1->show(); mitkWidget2->hide(); mitkWidget3->hide(); mitkWidget4->hide(); m_Layout = LAYOUT_WIDGET1; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_WIDGET1); mitkWidget2->LayoutDesignListChanged(LAYOUT_WIDGET1); mitkWidget3->LayoutDesignListChanged(LAYOUT_WIDGET1); mitkWidget4->LayoutDesignListChanged(LAYOUT_WIDGET1); // update Alle Widgets this->UpdateAllWidgets(); mitk::RenderingManager::GetInstance()->SetRenderWindowFocus(mitkWidget1->GetVtkRenderWindow()); } void QmitkStdMultiWidget::changeLayoutToWidget2() { SMW_INFO << "changing layout to big Widget2 ..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // add widget Splitter to main Splitter m_MainSplit->addWidget(mitkWidget2Container); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); if (mitkWidget2->isHidden()) mitkWidget2->show(); mitkWidget3->hide(); mitkWidget4->hide(); m_Layout = LAYOUT_WIDGET2; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_WIDGET2); mitkWidget2->LayoutDesignListChanged(LAYOUT_WIDGET2); mitkWidget3->LayoutDesignListChanged(LAYOUT_WIDGET2); mitkWidget4->LayoutDesignListChanged(LAYOUT_WIDGET2); // update Alle Widgets this->UpdateAllWidgets(); mitk::RenderingManager::GetInstance()->SetRenderWindowFocus(mitkWidget2->GetVtkRenderWindow()); } void QmitkStdMultiWidget::changeLayoutToWidget3() { SMW_INFO << "changing layout to big Widget3 ..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // add widget Splitter to main Splitter m_MainSplit->addWidget(mitkWidget3Container); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); if (mitkWidget3->isHidden()) mitkWidget3->show(); mitkWidget4->hide(); m_Layout = LAYOUT_WIDGET3; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_WIDGET3); mitkWidget2->LayoutDesignListChanged(LAYOUT_WIDGET3); mitkWidget3->LayoutDesignListChanged(LAYOUT_WIDGET3); mitkWidget4->LayoutDesignListChanged(LAYOUT_WIDGET3); // update Alle Widgets this->UpdateAllWidgets(); mitk::RenderingManager::GetInstance()->SetRenderWindowFocus(mitkWidget3->GetVtkRenderWindow()); } void QmitkStdMultiWidget::changeLayoutToRowWidget3And4() { SMW_INFO << "changing layout to Widget3 and 4 in a Row..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // add Widgets to splitter m_LayoutSplit->addWidget(mitkWidget3Container); m_LayoutSplit->addWidget(mitkWidget4Container); // set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_ROW_WIDGET_3_AND_4; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_ROW_WIDGET_3_AND_4); mitkWidget2->LayoutDesignListChanged(LAYOUT_ROW_WIDGET_3_AND_4); mitkWidget3->LayoutDesignListChanged(LAYOUT_ROW_WIDGET_3_AND_4); mitkWidget4->LayoutDesignListChanged(LAYOUT_ROW_WIDGET_3_AND_4); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToColumnWidget3And4() { SMW_INFO << "changing layout to Widget3 and 4 in one Column..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // add Widgets to splitter m_LayoutSplit->addWidget(mitkWidget3Container); m_LayoutSplit->addWidget(mitkWidget4Container); // set SplitterSize QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets mitkWidget1->hide(); mitkWidget2->hide(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_COLUMN_WIDGET_3_AND_4; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_COLUMN_WIDGET_3_AND_4); mitkWidget2->LayoutDesignListChanged(LAYOUT_COLUMN_WIDGET_3_AND_4); mitkWidget3->LayoutDesignListChanged(LAYOUT_COLUMN_WIDGET_3_AND_4); mitkWidget4->LayoutDesignListChanged(LAYOUT_COLUMN_WIDGET_3_AND_4); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToRowWidgetSmall3andBig4() { SMW_INFO << "changing layout to Widget3 and 4 in a Row..." << std::endl; this->changeLayoutToRowWidget3And4(); m_Layout = LAYOUT_ROW_WIDGET_SMALL3_AND_BIG4; } void QmitkStdMultiWidget::changeLayoutToSmallUpperWidget2Big3and4() { SMW_INFO << "changing layout to Widget3 and 4 in a Row..." << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(Qt::Vertical, m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget into the splitters m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit2->addWidget(mitkWidget3Container); m_SubSplit2->addWidget(mitkWidget4Container); // set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit2->setSizes(splitterSize); splitterSize.clear(); splitterSize.push_back(500); splitterSize.push_back(1000); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt m_MainSplit->show(); // show Widget if hidden mitkWidget1->hide(); if (mitkWidget2->isHidden()) mitkWidget2->show(); if (mitkWidget3->isHidden()) mitkWidget3->show(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4); mitkWidget2->LayoutDesignListChanged(LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4); mitkWidget3->LayoutDesignListChanged(LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4); mitkWidget4->LayoutDesignListChanged(LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutTo2x2Dand3DWidget() { SMW_INFO << "changing layout to 2 x 2D and 3D Widget" << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(Qt::Vertical, m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // add Widgets to splitter m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget2Container); m_SubSplit2->addWidget(mitkWidget4Container); // set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); mitkWidget3->hide(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_2X_2D_AND_3D_WIDGET; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2X_2D_AND_3D_WIDGET); mitkWidget2->LayoutDesignListChanged(LAYOUT_2X_2D_AND_3D_WIDGET); mitkWidget3->LayoutDesignListChanged(LAYOUT_2X_2D_AND_3D_WIDGET); mitkWidget4->LayoutDesignListChanged(LAYOUT_2X_2D_AND_3D_WIDGET); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutToLeft2Dand3DRight2D() { SMW_INFO << "changing layout to 2D and 3D left, 2D right Widget" << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(Qt::Vertical, m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // add Widgets to splitter m_SubSplit1->addWidget(mitkWidget1Container); m_SubSplit1->addWidget(mitkWidget4Container); m_SubSplit2->addWidget(mitkWidget2Container); // set Splitter Size QList splitterSize; splitterSize.push_back(1000); splitterSize.push_back(1000); m_SubSplit1->setSizes(splitterSize); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt and add to Layout m_MainSplit->show(); // show/hide Widgets if (mitkWidget1->isHidden()) mitkWidget1->show(); if (mitkWidget2->isHidden()) mitkWidget2->show(); mitkWidget3->hide(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET); mitkWidget2->LayoutDesignListChanged(LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET); mitkWidget3->LayoutDesignListChanged(LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET); mitkWidget4->LayoutDesignListChanged(LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET); // update Alle Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::changeLayoutTo2DUpAnd3DDown() { SMW_INFO << "changing layout to 2D up and 3D down" << std::endl; // Hide all Menu Widgets this->HideAllWidgetToolbars(); delete QmitkStdMultiWidgetLayout; // create Main Layout QmitkStdMultiWidgetLayout = new QHBoxLayout(this); // Set Layout to widget this->setLayout(QmitkStdMultiWidgetLayout); // create main splitter m_MainSplit = new QSplitter(this); QmitkStdMultiWidgetLayout->addWidget(m_MainSplit); // create m_LayoutSplit and add to the mainSplit m_LayoutSplit = new QSplitter(Qt::Vertical, m_MainSplit); m_MainSplit->addWidget(m_LayoutSplit); // add LevelWindow Widget to mainSplitter m_MainSplit->addWidget(levelWindowWidget); // create m_SubSplit1 and m_SubSplit2 m_SubSplit1 = new QSplitter(m_LayoutSplit); m_SubSplit2 = new QSplitter(m_LayoutSplit); // insert Widget Container into splitter top m_SubSplit1->addWidget(mitkWidget1Container); // set SplitterSize for splitter top QList splitterSize; // insert Widget Container into splitter bottom m_SubSplit2->addWidget(mitkWidget4Container); // set SplitterSize for splitter m_LayoutSplit splitterSize.clear(); splitterSize.push_back(700); splitterSize.push_back(700); m_LayoutSplit->setSizes(splitterSize); // show mainSplitt m_MainSplit->show(); // show/hide Widgets if (mitkWidget1->isHidden()) mitkWidget1->show(); mitkWidget2->hide(); mitkWidget3->hide(); if (mitkWidget4->isHidden()) mitkWidget4->show(); m_Layout = LAYOUT_2D_UP_AND_3D_DOWN; // update Layout Design List mitkWidget1->LayoutDesignListChanged(LAYOUT_2D_UP_AND_3D_DOWN); mitkWidget2->LayoutDesignListChanged(LAYOUT_2D_UP_AND_3D_DOWN); mitkWidget3->LayoutDesignListChanged(LAYOUT_2D_UP_AND_3D_DOWN); mitkWidget4->LayoutDesignListChanged(LAYOUT_2D_UP_AND_3D_DOWN); // update all Widgets this->UpdateAllWidgets(); } void QmitkStdMultiWidget::SetDataStorage(mitk::DataStorage *ds) { if (ds == m_DataStorage) { return; } mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->SetDataStorage(ds); mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->SetDataStorage(ds); mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->SetDataStorage(ds); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->SetDataStorage(ds); m_DataStorage = ds; } void QmitkStdMultiWidget::Fit() { vtkSmartPointer vtkrenderer; vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); vtkrenderer = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->GetVtkRenderer(); if (vtkrenderer != nullptr) vtkrenderer->ResetCamera(); mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow())->GetCameraController()->Fit(); mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow())->GetCameraController()->Fit(); int w = vtkObject::GetGlobalWarningDisplay(); vtkObject::GlobalWarningDisplayOff(); vtkObject::SetGlobalWarningDisplay(w); } void QmitkStdMultiWidget::InitPositionTracking() { // TODO POSITIONTRACKER } void QmitkStdMultiWidget::AddDisplayPlaneSubTree() { // add the displayed planes of the multiwidget to a node to which the subtree // @a planesSubTree points ... mitk::PlaneGeometryDataMapper2D::Pointer mapper; // ... of widget 1 mitk::BaseRenderer *renderer1 = mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow()); m_PlaneNode1 = renderer1->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode1->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode1->SetProperty("name", mitk::StringProperty::New(std::string(renderer1->GetName()) + ".plane")); m_PlaneNode1->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode1->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode1->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 2 mitk::BaseRenderer *renderer2 = mitk::BaseRenderer::GetInstance(mitkWidget2->GetRenderWindow()); m_PlaneNode2 = renderer2->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode2->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode2->SetProperty("name", mitk::StringProperty::New(std::string(renderer2->GetName()) + ".plane")); m_PlaneNode2->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode2->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode2->SetMapper(mitk::BaseRenderer::Standard2D, mapper); // ... of widget 3 mitk::BaseRenderer *renderer3 = mitk::BaseRenderer::GetInstance(mitkWidget3->GetRenderWindow()); m_PlaneNode3 = renderer3->GetCurrentWorldPlaneGeometryNode(); m_PlaneNode3->SetProperty("visible", mitk::BoolProperty::New(true)); m_PlaneNode3->SetProperty("name", mitk::StringProperty::New(std::string(renderer3->GetName()) + ".plane")); m_PlaneNode3->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_PlaneNode3->SetProperty("helper object", mitk::BoolProperty::New(true)); mapper = mitk::PlaneGeometryDataMapper2D::New(); m_PlaneNode3->SetMapper(mitk::BaseRenderer::Standard2D, mapper); m_ParentNodeForGeometryPlanes = mitk::DataNode::New(); m_ParentNodeForGeometryPlanes->SetProperty("name", mitk::StringProperty::New("Widgets")); m_ParentNodeForGeometryPlanes->SetProperty("helper object", mitk::BoolProperty::New(true)); } mitk::SliceNavigationController *QmitkStdMultiWidget::GetTimeNavigationController() { return m_TimeNavigationController; } void QmitkStdMultiWidget::EnableStandardLevelWindow() { levelWindowWidget->disconnect(this); levelWindowWidget->SetDataStorage(mitk::BaseRenderer::GetInstance(mitkWidget1->GetRenderWindow())->GetDataStorage()); levelWindowWidget->show(); } void QmitkStdMultiWidget::DisableStandardLevelWindow() { levelWindowWidget->disconnect(this); levelWindowWidget->hide(); } // CAUTION: Legacy code for enabling Qt-signal-controlled view initialization. // Use RenderingManager::InitializeViews() instead. bool QmitkStdMultiWidget::InitializeStandardViews(const mitk::Geometry3D *geometry) { return m_RenderingManager->InitializeViews(geometry); } void QmitkStdMultiWidget::RequestUpdate() { m_RenderingManager->RequestUpdate(mitkWidget1->GetRenderWindow()); m_RenderingManager->RequestUpdate(mitkWidget2->GetRenderWindow()); m_RenderingManager->RequestUpdate(mitkWidget3->GetRenderWindow()); m_RenderingManager->RequestUpdate(mitkWidget4->GetRenderWindow()); } void QmitkStdMultiWidget::ForceImmediateUpdate() { m_RenderingManager->ForceImmediateUpdate(mitkWidget1->GetRenderWindow()); m_RenderingManager->ForceImmediateUpdate(mitkWidget2->GetRenderWindow()); m_RenderingManager->ForceImmediateUpdate(mitkWidget3->GetRenderWindow()); m_RenderingManager->ForceImmediateUpdate(mitkWidget4->GetRenderWindow()); } void QmitkStdMultiWidget::wheelEvent(QWheelEvent *e) { emit WheelMoved(e); } -void QmitkStdMultiWidget::mousePressEvent(QMouseEvent *e) +void QmitkStdMultiWidget::mousePressEvent(QMouseEvent *) { } void QmitkStdMultiWidget::moveEvent(QMoveEvent *e) { QWidget::moveEvent(e); // it is necessary to readjust the position of the Annotation as the StdMultiWidget has moved // unfortunately it's not done by QmitkRenderWindow::moveEvent -> must be done here emit Moved(); } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow1() const { return mitkWidget1; } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow2() const { return mitkWidget2; } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow3() const { return mitkWidget3; } QmitkRenderWindow *QmitkStdMultiWidget::GetRenderWindow4() const { return mitkWidget4; } const mitk::Point3D QmitkStdMultiWidget::GetCrossPosition() const { const mitk::PlaneGeometry *plane1 = mitkWidget1->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry *plane2 = mitkWidget2->GetSliceNavigationController()->GetCurrentPlaneGeometry(); const mitk::PlaneGeometry *plane3 = mitkWidget3->GetSliceNavigationController()->GetCurrentPlaneGeometry(); mitk::Line3D line; if ((plane1 != nullptr) && (plane2 != nullptr) && (plane1->IntersectionLine(plane2, line))) { mitk::Point3D point; if ((plane3 != nullptr) && (plane3->IntersectionPoint(line, point))) { return point; } } // TODO BUG POSITIONTRACKER; mitk::Point3D p; return p; // return m_LastLeftClickPositionSupplier->GetCurrentPoint(); } void QmitkStdMultiWidget::EnablePositionTracking() { } void QmitkStdMultiWidget::DisablePositionTracking() { } void QmitkStdMultiWidget::EnsureDisplayContainsPoint(mitk::BaseRenderer *renderer, const mitk::Point3D &p) { mitk::Point2D pointOnDisplay; renderer->WorldToDisplay(p, pointOnDisplay); if (pointOnDisplay[0] < renderer->GetVtkRenderer()->GetOrigin()[0] || pointOnDisplay[1] < renderer->GetVtkRenderer()->GetOrigin()[1] || pointOnDisplay[0] > renderer->GetVtkRenderer()->GetOrigin()[0] + renderer->GetViewportSize()[0] || pointOnDisplay[1] > renderer->GetVtkRenderer()->GetOrigin()[1] + renderer->GetViewportSize()[1]) { mitk::Point2D pointOnPlane; renderer->GetCurrentWorldPlaneGeometry()->Map(p, pointOnPlane); renderer->GetCameraController()->MoveCameraToPoint(pointOnPlane); } } void QmitkStdMultiWidget::MoveCrossToPosition(const mitk::Point3D &newPosition) { mitkWidget1->GetSliceNavigationController()->SelectSliceByPoint(newPosition); mitkWidget2->GetSliceNavigationController()->SelectSliceByPoint(newPosition); mitkWidget3->GetSliceNavigationController()->SelectSliceByPoint(newPosition); m_RenderingManager->RequestUpdateAll(); } void QmitkStdMultiWidget::HandleCrosshairPositionEvent() { if (!m_PendingCrosshairPositionEvent) { m_PendingCrosshairPositionEvent = true; QTimer::singleShot(0, this, SLOT(HandleCrosshairPositionEventDelayed())); } } mitk::DataNode::Pointer QmitkStdMultiWidget::GetTopLayerNode(mitk::DataStorage::SetOfObjects::ConstPointer nodes) { mitk::Point3D crosshairPos = this->GetCrossPosition(); mitk::DataNode::Pointer node; int maxlayer = -32768; if (nodes.IsNotNull()) { mitk::BaseRenderer *baseRenderer = this->mitkWidget1->GetSliceNavigationController()->GetRenderer(); // find node with largest layer, that is the node shown on top in the render window for (unsigned int x = 0; x < nodes->size(); x++) { if ((nodes->at(x)->GetData()->GetGeometry() != nullptr) && nodes->at(x)->GetData()->GetGeometry()->IsInside(crosshairPos)) { int layer = 0; if (!(nodes->at(x)->GetIntProperty("layer", layer))) continue; if (layer > maxlayer) { if (static_cast(nodes->at(x))->IsVisible(baseRenderer)) { node = nodes->at(x); maxlayer = layer; } } } } } return node; } void QmitkStdMultiWidget::HandleCrosshairPositionEventDelayed() { m_PendingCrosshairPositionEvent = false; // find image with highest layer mitk::TNodePredicateDataType::Pointer isImageData = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer nodes = this->m_DataStorage->GetSubset(isImageData).GetPointer(); mitk::DataNode::Pointer node; mitk::DataNode::Pointer topSourceNode; mitk::Image::Pointer image; bool isBinary = false; node = this->GetTopLayerNode(nodes); int component = 0; if (node.IsNotNull()) { node->GetBoolProperty("binary", isBinary); if (isBinary) { mitk::DataStorage::SetOfObjects::ConstPointer sourcenodes = m_DataStorage->GetSources(node, nullptr, true); if (!sourcenodes->empty()) { topSourceNode = this->GetTopLayerNode(sourcenodes); } if (topSourceNode.IsNotNull()) { image = dynamic_cast(topSourceNode->GetData()); topSourceNode->GetIntProperty("Image.Displayed Component", component); } else { image = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } else { image = dynamic_cast(node->GetData()); node->GetIntProperty("Image.Displayed Component", component); } } mitk::Point3D crosshairPos = this->GetCrossPosition(); std::string statusText; std::stringstream stream; itk::Index<3> p; mitk::BaseRenderer *baseRenderer = this->mitkWidget1->GetSliceNavigationController()->GetRenderer(); unsigned int timestep = baseRenderer->GetTimeStep(); if (image.IsNotNull() && (image->GetTimeSteps() > timestep)) { image->GetGeometry()->WorldToIndex(crosshairPos, p); stream.precision(2); stream << "Position: <" << std::fixed << crosshairPos[0] << ", " << std::fixed << crosshairPos[1] << ", " << std::fixed << crosshairPos[2] << "> mm"; stream << "; Index: <" << p[0] << ", " << p[1] << ", " << p[2] << "> "; mitk::ScalarType pixelValue; mitkPixelTypeMultiplex5(mitk::FastSinglePixelAccess, image->GetChannelDescriptor().GetPixelType(), image, image->GetVolumeData(baseRenderer->GetTimeStep()), p, pixelValue, component); if (fabs(pixelValue) > 1000000 || fabs(pixelValue) < 0.01) { stream << "; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << std::scientific << pixelValue << " "; } else { stream << "; Time: " << baseRenderer->GetTime() << " ms; Pixelvalue: " << pixelValue << " "; } } else { stream << "No image information at this position!"; } statusText = stream.str(); mitk::StatusBar::GetInstance()->DisplayGreyValueText(statusText.c_str()); } int QmitkStdMultiWidget::GetLayout() const { return m_Layout; } bool QmitkStdMultiWidget::GetGradientBackgroundFlag() const { return m_GradientBackgroundFlag; } void QmitkStdMultiWidget::EnableGradientBackground() { // gradient background is by default only in widget 4, otherwise // interferences between 2D rendering and VTK rendering may occur. for (unsigned int i = 0; i < 4; ++i) { GetRenderWindow(i)->GetRenderer()->GetVtkRenderer()->GradientBackgroundOn(); } m_GradientBackgroundFlag = true; } void QmitkStdMultiWidget::DisableGradientBackground() { for (unsigned int i = 0; i < 4; ++i) { GetRenderWindow(i)->GetRenderer()->GetVtkRenderer()->GradientBackgroundOff(); } m_GradientBackgroundFlag = false; } void QmitkStdMultiWidget::EnableDepartmentLogo() { m_LogoRendering->SetVisibility(true); RequestUpdate(); } void QmitkStdMultiWidget::DisableDepartmentLogo() { m_LogoRendering->SetVisibility(false); RequestUpdate(); } bool QmitkStdMultiWidget::IsDepartmentLogoEnabled() const { return m_LogoRendering->IsVisible(); } void QmitkStdMultiWidget::SetWidgetPlaneVisibility(const char *widgetName, bool visible, mitk::BaseRenderer *renderer) { if (m_DataStorage.IsNotNull()) { mitk::DataNode *n = m_DataStorage->GetNamedNode(widgetName); if (n != nullptr) n->SetVisibility(visible, renderer); } } void QmitkStdMultiWidget::SetWidgetPlanesVisibility(bool visible, mitk::BaseRenderer *renderer) { if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetVisibility(visible, renderer); } if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetVisibility(visible, renderer); } if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetVisibility(visible, renderer); } m_RenderingManager->RequestUpdateAll(); } void QmitkStdMultiWidget::SetWidgetPlanesLocked(bool locked) { // do your job and lock or unlock slices. GetRenderWindow1()->GetSliceNavigationController()->SetSliceLocked(locked); GetRenderWindow2()->GetSliceNavigationController()->SetSliceLocked(locked); GetRenderWindow3()->GetSliceNavigationController()->SetSliceLocked(locked); } void QmitkStdMultiWidget::SetWidgetPlanesRotationLocked(bool locked) { // do your job and lock or unlock slices. GetRenderWindow1()->GetSliceNavigationController()->SetSliceRotationLocked(locked); GetRenderWindow2()->GetSliceNavigationController()->SetSliceRotationLocked(locked); GetRenderWindow3()->GetSliceNavigationController()->SetSliceRotationLocked(locked); } void QmitkStdMultiWidget::SetWidgetPlanesRotationLinked(bool link) { emit WidgetPlanesRotationLinked(link); } void QmitkStdMultiWidget::SetWidgetPlaneMode(int userMode) { MITK_DEBUG << "Changing crosshair mode to " << userMode; emit WidgetNotifyNewCrossHairMode(userMode); // Convert user interface mode to actual mode { switch (userMode) { case 0: m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::MITK); break; case 1: m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::ROTATION); break; case 2: m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::ROTATIONLINKED); break; case 3: m_MouseModeSwitcher->SetInteractionScheme(mitk::MouseModeSwitcher::InteractionScheme::SWIVEL); break; } } } void QmitkStdMultiWidget::SetGradientBackgroundColorForRenderWindow(const mitk::Color &upper, const mitk::Color &lower, unsigned int widgetNumber) { if (widgetNumber > 3) { MITK_ERROR << "Gradientbackground for unknown widget!"; return; } m_GradientBackgroundColors[widgetNumber].first = upper; m_GradientBackgroundColors[widgetNumber].second = lower; vtkRenderer *renderer = GetRenderWindow(widgetNumber)->GetRenderer()->GetVtkRenderer(); renderer->SetBackground2(upper[0], upper[1], upper[2]); renderer->SetBackground(lower[0], lower[1], lower[2]); m_GradientBackgroundFlag = true; } void QmitkStdMultiWidget::SetGradientBackgroundColors(const mitk::Color &upper, const mitk::Color &lower) { for (unsigned int i = 0; i < 4; ++i) { vtkRenderer *renderer = GetRenderWindow(i)->GetRenderer()->GetVtkRenderer(); renderer->SetBackground2(upper[0], upper[1], upper[2]); renderer->SetBackground(lower[0], lower[1], lower[2]); } m_GradientBackgroundFlag = true; } void QmitkStdMultiWidget::SetDepartmentLogoPath(const char *path) { m_LogoRendering->SetLogoImagePath(path); mitk::BaseRenderer *renderer = mitk::BaseRenderer::GetInstance(mitkWidget4->GetRenderWindow()); m_LogoRendering->Update(renderer); RequestUpdate(); } void QmitkStdMultiWidget::SetWidgetPlaneModeToSlicing(bool activate) { if (activate) { this->SetWidgetPlaneMode(PLANE_MODE_SLICING); } } void QmitkStdMultiWidget::SetWidgetPlaneModeToRotation(bool activate) { if (activate) { this->SetWidgetPlaneMode(PLANE_MODE_ROTATION); } } void QmitkStdMultiWidget::SetWidgetPlaneModeToSwivel(bool activate) { if (activate) { this->SetWidgetPlaneMode(PLANE_MODE_SWIVEL); } } void QmitkStdMultiWidget::OnLayoutDesignChanged(int layoutDesignIndex) { switch (layoutDesignIndex) { case LAYOUT_DEFAULT: { this->changeLayoutToDefault(); break; } case LAYOUT_2D_IMAGES_UP: { this->changeLayoutTo2DImagesUp(); break; } case LAYOUT_2D_IMAGES_LEFT: { this->changeLayoutTo2DImagesLeft(); break; } case LAYOUT_BIG_3D: { this->changeLayoutToBig3D(); break; } case LAYOUT_WIDGET1: { this->changeLayoutToWidget1(); break; } case LAYOUT_WIDGET2: { this->changeLayoutToWidget2(); break; } case LAYOUT_WIDGET3: { this->changeLayoutToWidget3(); break; } case LAYOUT_2X_2D_AND_3D_WIDGET: { this->changeLayoutTo2x2Dand3DWidget(); break; } case LAYOUT_ROW_WIDGET_3_AND_4: { this->changeLayoutToRowWidget3And4(); break; } case LAYOUT_COLUMN_WIDGET_3_AND_4: { this->changeLayoutToColumnWidget3And4(); break; } case LAYOUT_ROW_WIDGET_SMALL3_AND_BIG4: { this->changeLayoutToRowWidgetSmall3andBig4(); break; } case LAYOUT_SMALL_UPPER_WIDGET2_BIG3_AND4: { this->changeLayoutToSmallUpperWidget2Big3and4(); break; } case LAYOUT_2D_AND_3D_LEFT_2D_RIGHT_WIDGET: { this->changeLayoutToLeft2Dand3DRight2D(); break; } }; } void QmitkStdMultiWidget::UpdateAllWidgets() { mitkWidget1->resize(mitkWidget1Container->frameSize().width() - 1, mitkWidget1Container->frameSize().height()); mitkWidget1->resize(mitkWidget1Container->frameSize().width(), mitkWidget1Container->frameSize().height()); mitkWidget2->resize(mitkWidget2Container->frameSize().width() - 1, mitkWidget2Container->frameSize().height()); mitkWidget2->resize(mitkWidget2Container->frameSize().width(), mitkWidget2Container->frameSize().height()); mitkWidget3->resize(mitkWidget3Container->frameSize().width() - 1, mitkWidget3Container->frameSize().height()); mitkWidget3->resize(mitkWidget3Container->frameSize().width(), mitkWidget3Container->frameSize().height()); mitkWidget4->resize(mitkWidget4Container->frameSize().width() - 1, mitkWidget4Container->frameSize().height()); mitkWidget4->resize(mitkWidget4Container->frameSize().width(), mitkWidget4Container->frameSize().height()); } void QmitkStdMultiWidget::HideAllWidgetToolbars() { mitkWidget1->HideRenderWindowMenu(); mitkWidget2->HideRenderWindowMenu(); mitkWidget3->HideRenderWindowMenu(); mitkWidget4->HideRenderWindowMenu(); } void QmitkStdMultiWidget::ActivateMenuWidget(bool state) { mitkWidget1->ActivateMenuWidget(state, this); mitkWidget2->ActivateMenuWidget(state, this); mitkWidget3->ActivateMenuWidget(state, this); mitkWidget4->ActivateMenuWidget(state, this); } bool QmitkStdMultiWidget::IsMenuWidgetEnabled() const { return mitkWidget1->GetActivateMenuWidgetFlag(); } void QmitkStdMultiWidget::SetDecorationColor(unsigned int widgetNumber, mitk::Color color) { switch (widgetNumber) { case 0: if (m_PlaneNode1.IsNotNull()) { m_PlaneNode1->SetColor(color); } break; case 1: if (m_PlaneNode2.IsNotNull()) { m_PlaneNode2->SetColor(color); } break; case 2: if (m_PlaneNode3.IsNotNull()) { m_PlaneNode3->SetColor(color); } break; case 3: m_DecorationColorWidget4 = color; break; default: MITK_ERROR << "Decoration color for unknown widget!"; break; } } void QmitkStdMultiWidget::ResetCrosshair() { if (m_DataStorage.IsNotNull()) { m_RenderingManager->InitializeViewsByBoundingObjects(m_DataStorage); // m_RenderingManager->InitializeViews( m_DataStorage->ComputeVisibleBoundingGeometry3D() ); // reset interactor to normal slicing this->SetWidgetPlaneMode(PLANE_MODE_SLICING); } } void QmitkStdMultiWidget::EnableColoredRectangles() { m_RectangleProps[0]->SetVisibility(1); m_RectangleProps[1]->SetVisibility(1); m_RectangleProps[2]->SetVisibility(1); m_RectangleProps[3]->SetVisibility(1); } void QmitkStdMultiWidget::DisableColoredRectangles() { m_RectangleProps[0]->SetVisibility(0); m_RectangleProps[1]->SetVisibility(0); m_RectangleProps[2]->SetVisibility(0); m_RectangleProps[3]->SetVisibility(0); } bool QmitkStdMultiWidget::IsColoredRectanglesEnabled() const { return m_RectangleProps[0]->GetVisibility() > 0; } mitk::MouseModeSwitcher *QmitkStdMultiWidget::GetMouseModeSwitcher() { return m_MouseModeSwitcher; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane1() { return this->m_PlaneNode1; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane2() { return this->m_PlaneNode2; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane3() { return this->m_PlaneNode3; } mitk::DataNode::Pointer QmitkStdMultiWidget::GetWidgetPlane(int id) { switch (id) { case 1: return this->m_PlaneNode1; break; case 2: return this->m_PlaneNode2; break; case 3: return this->m_PlaneNode3; break; default: return nullptr; } } diff --git a/Modules/RTUI/Qmitk/QmitkDoseVisualStyleDelegate.cpp b/Modules/RTUI/Qmitk/QmitkDoseVisualStyleDelegate.cpp index 9de4f0b4c8..0abfd3973f 100644 --- a/Modules/RTUI/Qmitk/QmitkDoseVisualStyleDelegate.cpp +++ b/Modules/RTUI/Qmitk/QmitkDoseVisualStyleDelegate.cpp @@ -1,94 +1,94 @@ /*=================================================================== 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 "QmitkDoseVisualStyleDelegate.h" #include #include #include #include #include static QRect CheckBoxRect(const QStyleOptionViewItem &view_item_style_options) { QStyleOptionButton check_box_style_option; QRect check_box_rect = QApplication::style()->subElementRect( QStyle::SE_CheckBoxIndicator, &check_box_style_option); QPoint check_box_point(view_item_style_options.rect.x() + view_item_style_options.rect.width() / 2 - check_box_rect.width() / 2, view_item_style_options.rect.y() + view_item_style_options.rect.height() / 2 - check_box_rect.height() / 2); return QRect(check_box_point, check_box_rect.size()); } QmitkDoseVisualStyleDelegate::QmitkDoseVisualStyleDelegate(QObject * /*parent*/) { } void QmitkDoseVisualStyleDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { bool checkValue = index.data(Qt::DisplayRole).toBool(); QStyleOptionButton BtnStyle; BtnStyle.state = QStyle::State_Enabled; if(checkValue) { BtnStyle.state |= QStyle::State_On; } else { BtnStyle.state |= QStyle::State_Off; } BtnStyle.direction = QApplication::layoutDirection(); BtnStyle.rect = CheckBoxRect(option); QApplication::style()->drawControl(QStyle::CE_CheckBox,&BtnStyle,painter ); } -bool QmitkDoseVisualStyleDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, +bool QmitkDoseVisualStyleDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &, const QModelIndex &index) { Q_ASSERT(event); Q_ASSERT(model); // make sure that the item is checkable Qt::ItemFlags flags = model->flags(index); if (!(flags & Qt::ItemIsEditable) || !(flags & Qt::ItemIsEnabled)) { return false; } // make sure that we have the right event type QMouseEvent* mouseEvent = dynamic_cast(event); if (!mouseEvent) { return false; } else { if (mouseEvent->type() != QEvent::MouseButtonRelease || mouseEvent->button() != Qt::LeftButton) { return false; } } bool newState = !(index.data(Qt::EditRole).toBool()); return model->setData(index, QVariant(newState), Qt::EditRole); }; diff --git a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp index 413cffdfe0..33a401c09f 100644 --- a/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp +++ b/Modules/Segmentation/Algorithms/mitkCorrectorAlgorithm.cpp @@ -1,497 +1,491 @@ /*=================================================================== 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 "mitkCorrectorAlgorithm.h" #include "mitkContourUtils.h" #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageDataItem.h" #include "mitkLegacyAdaptors.h" #include #include "itkCastImageFilter.h" #include "itkImageDuplicator.h" #include "itkImageRegionIterator.h" mitk::CorrectorAlgorithm::CorrectorAlgorithm() : ImageToImageFilter(), m_FillColor(1), m_EraseColor(0) { } mitk::CorrectorAlgorithm::~CorrectorAlgorithm() { } template void ConvertBackToCorrectPixelType( - itk::Image *reference, + itk::Image *, mitk::Image::Pointer target, itk::Image::Pointer segmentationPixelTypeImage) { typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::CastImageFilter CastImageFilterType; typename CastImageFilterType::Pointer castImageFilter = CastImageFilterType::New(); castImageFilter->SetInput(segmentationPixelTypeImage); castImageFilter->Update(); typename OutputImageType::Pointer tempItkImage = castImageFilter->GetOutput(); tempItkImage->DisconnectPipeline(); mitk::CastToMitkImage(tempItkImage, target); } void mitk::CorrectorAlgorithm::GenerateData() { Image::Pointer inputImage = const_cast(ImageToImageFilter::GetInput(0)); if (inputImage.IsNull() || inputImage->GetDimension() != 2) { itkExceptionMacro("CorrectorAlgorithm needs a 2D image as input."); } if (m_Contour.IsNull()) { itkExceptionMacro("CorrectorAlgorithm needs a Contour object as input."); } // copy the input (since m_WorkingImage will be changed later) m_WorkingImage = inputImage; TimeGeometry::Pointer originalGeometry = nullptr; if (inputImage->GetTimeGeometry()) { originalGeometry = inputImage->GetTimeGeometry()->Clone(); m_WorkingImage->SetTimeGeometry(originalGeometry); } else { itkExceptionMacro("Original image does not have a 'Time sliced geometry'! Cannot copy."); } Image::Pointer temporarySlice; // Convert to DefaultSegmentationDataType (because TobiasHeimannCorrectionAlgorithm relys on that data type) { itk::Image::Pointer correctPixelTypeImage; CastToItkImage(m_WorkingImage, correctPixelTypeImage); assert(correctPixelTypeImage.IsNotNull()); // possible bug in CastToItkImage ? // direction maxtrix is wrong/broken/not working after CastToItkImage, leading to a failed assertion in // mitk/Core/DataStructures/mitkSlicedGeometry3D.cpp, 479: // virtual void mitk::SlicedGeometry3D::SetSpacing(const mitk::Vector3D&): Assertion `aSpacing[0]>0 && aSpacing[1]>0 // && aSpacing[2]>0' failed // solution here: we overwrite it with an unity matrix itk::Image::DirectionType imageDirection; imageDirection.SetIdentity(); // correctPixelTypeImage->SetDirection(imageDirection); temporarySlice = this->GetOutput(); // temporarySlice = ImportItkImage( correctPixelTypeImage ); // m_FillColor = 1; m_EraseColor = 0; ImprovedHeimannCorrectionAlgorithm(correctPixelTypeImage); // this is suboptimal, needs to be kept synchronous to DefaultSegmentationDataType if (inputImage->GetChannelDescriptor().GetPixelType().GetComponentType() == itk::ImageIOBase::USHORT) { // the cast at the beginning did not copy the data CastToMitkImage(correctPixelTypeImage, temporarySlice); } else { // it did copy the data and cast the pixel type AccessByItk_n(m_WorkingImage, ConvertBackToCorrectPixelType, (temporarySlice, correctPixelTypeImage)); } } temporarySlice->SetTimeGeometry(originalGeometry); } template itk::Index<2> mitk::CorrectorAlgorithm::ensureIndexInImage(ScalarType i0, ScalarType i1) { itk::Index<2> toReturn; itk::Size<5> size = m_WorkingImage->GetLargestPossibleRegion().GetSize(); toReturn[0] = std::min((ScalarType)(size[0] - 1), std::max((ScalarType)0.0, i0)); toReturn[1] = std::min((ScalarType)(size[1] - 1), std::max((ScalarType)0.0, i1)); return toReturn; } bool mitk::CorrectorAlgorithm::ImprovedHeimannCorrectionAlgorithm( itk::Image::Pointer pic) { /*! Some documentation (not by the original author) TobiasHeimannCorrectionAlgorithm will be called, when the user has finished drawing a freehand line. There should be different results, depending on the line's properties: 1. Without any prior segmentation, the start point and the end point of the drawn line will be connected to a contour and the area enclosed by the contour will be marked as segmentation. 2. When the whole line is inside a segmentation, start and end point will be connected to a contour and the area of this contour will be subtracted from the segmentation. 3. When the line starts inside a segmentation and ends outside with only a single transition from segmentation to no-segmentation, nothing will happen. 4. When there are multiple transitions between inside-segmentation and outside-segmentation, the line will be divided in so called segments. Each segment is either fully inside or fully outside a segmentation. When it is inside a segmentation, its enclosed area will be subtracted from the segmentation. When the segment is outside a segmentation, its enclosed area it will be added to the segmentation. The algorithm is described in full length in Tobias Heimann's diploma thesis (MBI Technical Report 145, p. 37 - 40). */ ContourModel::Pointer projectedContour = mitk::ContourModelUtils::ProjectContourTo2DSlice(m_WorkingImage, m_Contour, true, false); - bool firstPointIsFillingColor = false; - if (projectedContour.IsNull() || projectedContour->GetNumberOfVertices() < 2) - { return false; - } // Read the first point of the contour ContourModel::VertexIterator contourIter = projectedContour->Begin(); if (contourIter == projectedContour->End()) return false; itk::Index<2> previousIndex; previousIndex = ensureIndexInImage((*contourIter)->Coordinates[0], (*contourIter)->Coordinates[1]); ++contourIter; int currentColor = (pic->GetPixel(previousIndex) == m_FillColor); - firstPointIsFillingColor = currentColor; TSegData currentSegment; int countOfSegments = 1; bool firstSegment = true; ContourModel::VertexIterator contourEnd = projectedContour->End(); for (; contourIter != contourEnd; ++contourIter) { // Get current point itk::Index<2> currentIndex; currentIndex = ensureIndexInImage((*contourIter)->Coordinates[0] + 0.5, (*contourIter)->Coordinates[1] + 0.5); // Calculate length and slope double slopeX = currentIndex[0] - previousIndex[0]; double slopeY = currentIndex[1] - previousIndex[1]; double length = std::sqrt(slopeX * slopeX + slopeY * slopeY); double deltaX = slopeX / length; double deltaY = slopeY / length; for (double i = 0; i <= length && length > 0; i += 1) { itk::Index<2> temporaryIndex; temporaryIndex = ensureIndexInImage(previousIndex[0] + deltaX * i, previousIndex[1] + deltaY * i); if (!pic->GetLargestPossibleRegion().IsInside(temporaryIndex)) continue; if ((pic->GetPixel(temporaryIndex) == m_FillColor) != currentColor) { currentSegment.points.push_back(temporaryIndex); if (!firstSegment) { ModifySegment(currentSegment, pic); } else { firstSegment = false; } currentSegment = TSegData(); ++countOfSegments; currentColor = (pic->GetPixel(temporaryIndex) == m_FillColor); } currentSegment.points.push_back(temporaryIndex); } previousIndex = currentIndex; } return true; } void mitk::CorrectorAlgorithm::ColorSegment( const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image::Pointer pic) { int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor); int color = 0; if (colorMode) color = m_EraseColor; else color = m_FillColor; std::vector>::const_iterator indexIterator; std::vector>::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); for (; indexIterator != indexEnd; ++indexIterator) { pic->SetPixel(*indexIterator, color); } } itk::Image::Pointer mitk::CorrectorAlgorithm::CloneImage( itk::Image::Pointer pic) { typedef itk::Image ItkImageType; typedef itk::ImageDuplicator DuplicatorType; DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(pic); duplicator->Update(); return duplicator->GetOutput(); } itk::Index<2> mitk::CorrectorAlgorithm::GetFirstPoint( const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image::Pointer pic) { int colorMode = (pic->GetPixel(segment.points[0]) == m_FillColor); std::vector>::const_iterator indexIterator; std::vector>::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); itk::Index<2> index; for (; indexIterator != indexEnd; ++indexIterator) { for (int xOffset = -1; xOffset < 2; ++xOffset) { for (int yOffset = -1; yOffset < 2; ++yOffset) { index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset); if ((pic->GetPixel(index) == m_FillColor) != colorMode) { return index; } } } } mitkThrow() << "No Starting point is found next to the curve."; } std::vector> mitk::CorrectorAlgorithm::FindSeedPoints( const mitk::CorrectorAlgorithm::TSegData &segment, itk::Image::Pointer pic) { - typedef itk::Image ItkImageType; typedef itk::Image::Pointer ItkImagePointerType; std::vector> seedPoints; try { itk::Index<2> firstPoint = GetFirstPoint(segment, pic); seedPoints.push_back(firstPoint); } catch (mitk::Exception e) { return seedPoints; } if (segment.points.size() < 4) return seedPoints; std::vector>::const_iterator indexIterator; std::vector>::const_iterator indexEnd; indexIterator = segment.points.begin(); indexEnd = segment.points.end(); ItkImagePointerType listOfPoints = CloneImage(pic); listOfPoints->FillBuffer(0); listOfPoints->SetPixel(seedPoints[0], 1); for (; indexIterator != indexEnd; ++indexIterator) { listOfPoints->SetPixel(*indexIterator, 2); } indexIterator = segment.points.begin(); indexIterator++; indexIterator++; indexEnd--; indexEnd--; for (; indexIterator != indexEnd; ++indexIterator) { bool pointFound = true; while (pointFound) { pointFound = false; itk::Index<2> index; itk::Index<2> index2; for (int xOffset = -1; xOffset < 2; ++xOffset) { for (int yOffset = -1; yOffset < 2; ++yOffset) { index = ensureIndexInImage((*indexIterator)[0] - xOffset, (*indexIterator)[1] - yOffset); index2 = index; if (listOfPoints->GetPixel(index2) > 0) continue; index[0] = index[0] - 1; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } index[0] = index[0] + 2; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } index[0] = index[0] - 1; index[1] = index[1] - 1; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } index[1] = index[1] + 2; index = ensureIndexInImage(index[0], index[1]); if (listOfPoints->GetPixel(index) == 1) { pointFound = true; seedPoints.push_back(index2); listOfPoints->SetPixel(index2, 1); continue; } } } } } return seedPoints; } int mitk::CorrectorAlgorithm::FillRegion( const std::vector> &seedPoints, itk::Image::Pointer pic) { int numberOfPixel = 0; int mode = (pic->GetPixel(seedPoints[0]) == m_FillColor); int drawColor = m_FillColor; if (mode) { drawColor = m_EraseColor; } std::vector> workPoints; workPoints = seedPoints; // workPoints.push_back(seedPoints[0]); while (workPoints.size() > 0) { itk::Index<2> currentIndex = workPoints.back(); workPoints.pop_back(); if ((pic->GetPixel(currentIndex) == m_FillColor) == mode) ++numberOfPixel; pic->SetPixel(currentIndex, drawColor); currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1]); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); currentIndex = ensureIndexInImage(currentIndex[0] + 2, currentIndex[1]); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); currentIndex = ensureIndexInImage(currentIndex[0] - 1, currentIndex[1] - 1); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); currentIndex = ensureIndexInImage(currentIndex[0], currentIndex[1] + 2); if (pic->GetLargestPossibleRegion().IsInside(currentIndex) && (pic->GetPixel(currentIndex) == m_FillColor) == mode) workPoints.push_back(currentIndex); } return numberOfPixel; } void mitk::CorrectorAlgorithm::OverwriteImage( itk::Image::Pointer source, itk::Image::Pointer target) { typedef itk::Image ItkImageType; typedef itk::ImageRegionIterator ImageIteratorType; ImageIteratorType sourceIter(source, source->GetLargestPossibleRegion()); ImageIteratorType targetIter(target, target->GetLargestPossibleRegion()); while (!sourceIter.IsAtEnd()) { targetIter.Set(sourceIter.Get()); ++sourceIter; ++targetIter; } } bool mitk::CorrectorAlgorithm::ModifySegment(const TSegData &segment, itk::Image::Pointer pic) { typedef itk::Image::Pointer ItkImagePointerType; ItkImagePointerType firstSideImage = CloneImage(pic); ColorSegment(segment, firstSideImage); ItkImagePointerType secondSideImage = CloneImage(firstSideImage); std::vector> seedPoints = FindSeedPoints(segment, firstSideImage); if (seedPoints.size() < 1) return false; int firstSidePixel = FillRegion(seedPoints, firstSideImage); std::vector> secondSeedPoints = FindSeedPoints(segment, firstSideImage); if (secondSeedPoints.size() < 1) return false; int secondSidePixel = FillRegion(secondSeedPoints, secondSideImage); if (firstSidePixel < secondSidePixel) { OverwriteImage(firstSideImage, pic); } else { OverwriteImage(secondSideImage, pic); } return true; } diff --git a/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp b/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp index 0189188fce..623e1b17cc 100644 --- a/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp @@ -1,200 +1,198 @@ /*=================================================================== 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 "mitkFeatureBasedEdgeDetectionFilter.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::FeatureBasedEdgeDetectionFilter::FeatureBasedEdgeDetectionFilter() { this->SetNumberOfRequiredInputs(1); this->SetNumberOfIndexedOutputs(1); } mitk::FeatureBasedEdgeDetectionFilter::~FeatureBasedEdgeDetectionFilter() { } void mitk::FeatureBasedEdgeDetectionFilter::GenerateData() { mitk::Image::Pointer image = ImageToUnstructuredGridFilter::GetInput(); if (m_SegmentationMask.IsNull()) { MITK_WARN << "Please set a segmentation mask first" << std::endl; return; } // First create a threshold segmentation of the image. The threshold is determined // by the mean +/- stddev of the pixel values that are covered by the segmentation mask // Compute mean and stdDev based on the current segmentation mitk::ImageStatisticsCalculator::Pointer statCalc = mitk::ImageStatisticsCalculator::New(); statCalc->SetInputImage(image); mitk::ImageMaskGenerator::Pointer imgMask = mitk::ImageMaskGenerator::New(); imgMask->SetImageMask(m_SegmentationMask); mitk::ImageStatisticsCalculator::StatisticsContainer::Pointer stats = statCalc->GetStatistics(); double mean = stats->GetMean(); double stdDev = stats->GetStd(); double upperThreshold = mean + stdDev; double lowerThreshold = mean - stdDev; // Perform thresholding mitk::Image::Pointer thresholdImage = mitk::Image::New(); AccessByItk_3(image.GetPointer(), ITKThresholding, lowerThreshold, upperThreshold, thresholdImage) mitk::ProgressBar::GetInstance() ->Progress(2); // Postprocess threshold segmentation // First a closing will be executed mitk::Image::Pointer closedImage = mitk::Image::New(); AccessByItk_1(thresholdImage, ThreadedClosing, closedImage); // Then we will holes that might exist mitk::MorphologicalOperations::FillHoles(closedImage); mitk::ProgressBar::GetInstance()->Progress(); // Extract the binary edges of the resulting segmentation mitk::Image::Pointer edgeImage = mitk::Image::New(); AccessByItk_1(closedImage, ContourSearch, edgeImage); // Convert the edge image into an unstructured grid mitk::ImageToUnstructuredGridFilter::Pointer i2UFilter = mitk::ImageToUnstructuredGridFilter::New(); i2UFilter->SetInput(edgeImage); i2UFilter->SetThreshold(1.0); i2UFilter->Update(); m_PointGrid = this->GetOutput(); if (m_PointGrid.IsNull()) m_PointGrid = mitk::UnstructuredGrid::New(); m_PointGrid->SetVtkUnstructuredGrid(i2UFilter->GetOutput()->GetVtkUnstructuredGrid()); mitk::ProgressBar::GetInstance()->Progress(); } template void mitk::FeatureBasedEdgeDetectionFilter::ThreadedClosing(itk::Image *originalImage, mitk::Image::Pointer &result) { typedef itk::BinaryBallStructuringElement myKernelType; myKernelType ball; ball.SetRadius(1); ball.CreateStructuringElement(); typedef typename itk::Image ImageType; typename itk::DilateObjectMorphologyImageFilter::Pointer dilationFilter = itk::DilateObjectMorphologyImageFilter::New(); dilationFilter->SetInput(originalImage); dilationFilter->SetKernel(ball); dilationFilter->Update(); typename itk::Image::Pointer dilatedImage = dilationFilter->GetOutput(); typename itk::ErodeObjectMorphologyImageFilter::Pointer erodeFilter = itk::ErodeObjectMorphologyImageFilter::New(); erodeFilter->SetInput(dilatedImage); erodeFilter->SetKernel(ball); erodeFilter->Update(); mitk::GrabItkImageMemory(erodeFilter->GetOutput(), result); } template void mitk::FeatureBasedEdgeDetectionFilter::ContourSearch(itk::Image *originalImage, mitk::Image::Pointer &result) { typedef itk::Image ImageType; typedef itk::BinaryContourImageFilter binaryContourImageFilterType; - typedef unsigned short OutputPixelType; - typedef itk::Image OutputImageType; typename binaryContourImageFilterType::Pointer binaryContourFilter = binaryContourImageFilterType::New(); binaryContourFilter->SetInput(originalImage); binaryContourFilter->SetForegroundValue(1); binaryContourFilter->SetBackgroundValue(0); binaryContourFilter->Update(); typename itk::Image::Pointer itkImage = itk::Image::New(); itkImage->Graft(binaryContourFilter->GetOutput()); mitk::GrabItkImageMemory(itkImage, result); } template void mitk::FeatureBasedEdgeDetectionFilter::ITKThresholding(itk::Image *originalImage, double lower, double upper, mitk::Image::Pointer &result) { typedef itk::Image ImageType; typedef itk::Image SegmentationType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; if (typeid(TPixel) != typeid(float) && typeid(TPixel) != typeid(double)) { // round the thresholds if we have nor a float or double image lower = std::floor(lower + 0.5); upper = std::floor(upper - 0.5); } if (lower >= upper) { upper = lower; } typename ThresholdFilterType::Pointer filter = ThresholdFilterType::New(); filter->SetInput(originalImage); filter->SetLowerThreshold(lower); filter->SetUpperThreshold(upper); filter->SetInsideValue(1); filter->SetOutsideValue(0); filter->Update(); mitk::GrabItkImageMemory(filter->GetOutput(), result); } void mitk::FeatureBasedEdgeDetectionFilter::SetSegmentationMask(mitk::Image::Pointer segmentation) { this->m_SegmentationMask = segmentation; } void mitk::FeatureBasedEdgeDetectionFilter::GenerateOutputInformation() { Superclass::GenerateOutputInformation(); } diff --git a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp index d769ef0654..6cab65556f 100644 --- a/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkOverwriteDirectedPlaneImageFilter.cpp @@ -1,518 +1,515 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkApplyDiffImageOperation.h" #include "mitkDiffImageApplier.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkInteractionConst.h" #include "mitkOperationEvent.h" #include "mitkSegmentationInterpolationController.h" #include "mitkUndoController.h" #include #include mitk::OverwriteDirectedPlaneImageFilter::OverwriteDirectedPlaneImageFilter() : m_PlaneGeometry(nullptr), m_ImageGeometry3D(nullptr), m_TimeStep(0), m_Dimension0(0), m_Dimension1(1), m_CreateUndoInformation(false) { MITK_WARN << "Class is deprecated! Use mitkVtkImageOverwrite instead."; } mitk::OverwriteDirectedPlaneImageFilter::~OverwriteDirectedPlaneImageFilter() { } void mitk::OverwriteDirectedPlaneImageFilter::GenerateData() { // // this is the place to implement the major part of undo functionality (bug #491) // here we have to create undo/do operations // // WHO is the operation actor? This object may not be destroyed ever (design of undo stack)! // -> some singleton method of this filter? // // neccessary additional objects: // - something that executes the operations // - the operation class (must hold a binary diff or something) // - observer commands to know when the image is deleted (no further action then, perhaps even remove the operations // from the undo stack) // // Image::ConstPointer input = ImageToImageFilter::GetInput(0); Image::Pointer input3D = ImageToImageFilter::GetInput(0); // Image::ConstPointer slice = m_SliceImage; if (input3D.IsNull() || m_SliceImage.IsNull()) return; if (input3D->GetDimension() == 4) { ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(input3D); timeSelector->SetTimeNr(m_TimeStep); timeSelector->UpdateLargestPossibleRegion(); input3D = timeSelector->GetOutput(); } m_ImageGeometry3D = input3D->GetGeometry(); /* if ( m_SliceDifferenceImage.IsNull() || m_SliceDifferenceImage->GetDimension(0) != m_SliceImage->GetDimension(0) || m_SliceDifferenceImage->GetDimension(1) != m_SliceImage->GetDimension(1) ) { m_SliceDifferenceImage = mitk::Image::New(); mitk::PixelType pixelType( typeid(short signed int) ); m_SliceDifferenceImage->Initialize( pixelType, 2, m_SliceImage->GetDimensions() ); } */ // MITK_INFO << "Overwriting slice " << m_SliceIndex << " in dimension " << m_SliceDimension << " at time step " << // m_TimeStep << std::endl; // this will do a long long if/else to find out both pixel types /*AccessFixedDimensionByItk( input3D, ItkImageSwitch, 3 );*/ AccessFixedDimensionByItk(input3D, ItkSliceOverwriting, 3); // SegmentationInterpolationController* interpolator = SegmentationInterpolationController::InterpolatorForImage( // input3D ); // if (interpolator) //{ // interpolator->BlockModified(true); // //interpolator->SetChangedSlice( m_SliceDifferenceImage, m_SliceDimension, m_SliceIndex, m_TimeStep ); //} /* if ( m_CreateUndoInformation ) { // create do/undo operations (we don't execute the doOp here, because it has already been executed during calculation of the diff image ApplyDiffImageOperation* doOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); ApplyDiffImageOperation* undoOp = new ApplyDiffImageOperation( OpTEST, const_cast(input.GetPointer()), m_SliceDifferenceImage, m_TimeStep, m_SliceDimension, m_SliceIndex ); undoOp->SetFactor( -1.0 ); OperationEvent* undoStackItem = new OperationEvent( DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, this->EventDescription(m_SliceDimension, m_SliceIndex, m_TimeStep) ); UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); } */ // this image is modified (good to know for the renderer) input3D->Modified(); /*if (interpolator) { interpolator->BlockModified(false); }*/ } template void mitk::OverwriteDirectedPlaneImageFilter::ItkSliceOverwriting(itk::Image *input3D) { typedef itk::Image SliceImageType; - typedef itk::Image VolumeImageType; - - typedef itk::ImageSliceIteratorWithIndex OutputSliceIteratorType; typedef itk::ImageRegionConstIterator SliceIteratorType; typename SliceImageType::Pointer sliceImage = SliceImageType::New(); CastToItkImage(m_SliceImage, sliceImage); SliceIteratorType sliceIterator(sliceImage, sliceImage->GetLargestPossibleRegion()); sliceIterator.GoToBegin(); Point3D currentPointIn2D; Point3D worldPointIn3D; // Here we just iterate over the slice which must be written into the 3D volumen and set the corresponding pixel in // our 3D volume while (!sliceIterator.IsAtEnd()) { currentPointIn2D[0] = sliceIterator.GetIndex()[0] + 0.5; currentPointIn2D[1] = sliceIterator.GetIndex()[1] + 0.5; currentPointIn2D[2] = 0; m_PlaneGeometry->IndexToWorld(currentPointIn2D, worldPointIn3D); typename itk::Image::IndexType outputIndex; m_ImageGeometry3D->WorldToIndex(worldPointIn3D, outputIndex); // Only access ITK image if it's inside if (m_ImageGeometry3D->IsIndexInside(outputIndex)) { input3D->SetPixel(outputIndex, (TPixel)sliceIterator.Get()); } ++sliceIterator; } } /****TEST***/ // Maybe a bit more efficient but doesn`t already work. See also ExtractCirectedPlaneImageFilter // typename itk::Image::IndexType outputIndex; // if ( columns == extent[0] ) //{ ////If we are at the end of a row, then we have to go to the beginning of the next row // currentImagePointIn3D = origin; // currentImagePointIn3D += spacing[1]*bottom*currentPointIn2D[1]; // columns = 0; // m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); //} // else //{ // if ( columns != 0 ) //{ // currentImagePointIn3D += spacing[0]*right; //} // m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); //} // if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) //{ // outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); //} // else if (currentImagePointIn3D == origin) //{ // Point3D temp; // temp[0] = bottom[0]*spacing[0]*0.5; // temp[1] = bottom[1]*spacing[1]*0.5; // temp[2] = bottom[2]*spacing[2]*0.5; // origin[0] += temp[0]; // origin[1] += temp[1]; // origin[2] += temp[2]; // currentImagePointIn3D = origin; // m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); // if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) //{ // outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); //} //} // columns++; /****TEST ENDE****/ //* // // Offset the world coordinate by one pixel to compensate for // // index/world origin differences. // Point3D offsetIndex; // offsetIndex.Fill( 1 ); // Point3D offsetWorld; // offsetWorld.Fill( 0 ); // m_PlaneGeometry->IndexToWorld( offsetIndex, offsetWorld ); // // remove origin shift // const Point3D origin = m_PlaneGeometry->GetOrigin(); // offsetWorld[0] -= origin[0]; // offsetWorld[1] -= origin[1]; // offsetWorld[2] -= origin[2]; // // offset world coordinate // worldPointIn3D[ 0 ] += offsetWorld[0]; // worldPointIn3D[ 1 ] += offsetWorld[1]; // worldPointIn3D[ 2 ] += offsetWorld[2]; //*/ // basically copied from mitk/Core/Algorithms/mitkImageAccessByItk.h /*#define myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, pixeltype, dimension, itkimage2) \ // if ( typeId == typeid(pixeltype) ) \ //{ \ // typedef itk::Image ImageType; \ // typedef mitk::ImageToItk ImageToItkType; \ // itk::SmartPointer imagetoitk = ImageToItkType::New(); \ // imagetoitk->SetInput(mitkImage); \ // imagetoitk->Update(); \ // itkImageTypeFunction(imagetoitk->GetOutput(), itkimage2); \ //} // //#define myMITKOverwriteDirectedPlaneImageFilterAccessAllTypesByItk(mitkImage, itkImageTypeFunction, dimension, itkimage2) \ //{ \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, double, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, float, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, int, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned int, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, short, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned short, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, char, dimension, itkimage2) else \ // myMITKOverwriteDirectedPlaneImageFilterAccessByItk(mitkImage, itkImageTypeFunction, unsigned char, dimension, itkimage2) \ //}*/ // // // template // void mitk::OverwriteDirectedPlaneImageFilter::ItkImageSwitch( itk::Image* itkImage ) //{ // const std::type_info& typeId=*(m_SliceImage->GetPixelType().GetTypeId()); // // myMITKOverwriteDirectedPlaneImageFilterAccessAllTypesByItk( m_SliceImage, ItkImageProcessing, 2, itkImage ); //} // template // void mitk::OverwriteDirectedPlaneImageFilter::ItkImageProcessing( itk::Image* inputImage, // itk::Image* outputImage ) //{ // typedef itk::Image SliceImageType; // typedef itk::Image DiffImageType; // typedef itk::Image VolumeImageType; // // typedef itk::ImageSliceIteratorWithIndex< VolumeImageType > OutputSliceIteratorType; // typedef itk::ImageRegionConstIterator< SliceImageType > InputSliceIteratorType; // //typedef itk::ImageRegionIterator< DiffImageType > DiffSliceIteratorType; // // InputSliceIteratorType inputIterator( inputImage, inputImage->GetLargestPossibleRegion() ); // // //typename DiffImageType::Pointer diffImage; // //CastToItkImage( m_SliceDifferenceImage, diffImage ); // //DiffSliceIteratorType diffIterator( diffImage, diffImage->GetLargestPossibleRegion() ); // // inputIterator.GoToBegin(); // //diffIterator.GoToBegin(); // // //TEST // Point3D origin = m_PlaneGeometry->GetOrigin(); // Vector3D right = m_PlaneGeometry->GetAxisVector(0); // Vector3D bottom = m_PlaneGeometry->GetAxisVector(1); // right.Normalize(); // bottom.Normalize(); // // Vector2D spacing = inputImage->GetSpacing(); // // Vector2D extentInMM; // extentInMM[0] = m_PlaneGeometry->GetExtentInMM(0); // extentInMM[1] = m_PlaneGeometry->GetExtentInMM(1); // // Vector2D extent; // extent[0] = m_PlaneGeometry->GetExtent(0); // extent[1] = m_PlaneGeometry->GetExtent(1); // //TEST ENDE // // Point3D currentPointIn2D, worldPointIn3D; // TPixel2 outputPixel = 0; // // int debugCounter( 0 ); // // std::ofstream geometryFile; // geometryFile.precision(30); // geometryFile.open("C:/Users/fetzer/Desktop/TEST/geometryFileOv.txt"); // // geometryFile<<"Offset: [ "<GetIndexToWorldTransform()->GetOffset()[0]<<", // "<GetIndexToWorldTransform()->GetOffset()[1]<<", // "<GetIndexToWorldTransform()->GetOffset()[2]<<" ]"<GetIndexToWorldTransform()->GetMatrix()<IndexToWorld( currentPointIn2D, worldPointIn3D ); // // typename itk::Image::IndexType outputIndex; // m_ImageGeometry3D->WorldToIndex( worldPointIn3D, outputIndex ); // // // Only access ITK image if it's inside // if ( m_ImageGeometry3D->IsIndexInside( outputIndex ) ) // { // //outputPixel = outputImage->GetPixel( outputIndex ); // outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); // /*if( inputIterator.Get() == mitk::paint::addPixelValue ) // { // outputImage->SetPixel( outputIndex, (TPixel2)( 1 ) ); // } // else if( inputIterator.Get() == mitk::paint::subPixelValue ) // { // outputImage->SetPixel( outputIndex, (TPixel2)( 0 ) ); // }*/ // } // ///****TEST***/ // ////typename itk::Image::IndexType outputIndex; // ////if ( columns == extent[0] ) ////{ //////If we are at the end of a row, then we have to go to the beginning of the next row ////currentImagePointIn3D = origin; ////currentImagePointIn3D += spacing[1]*bottom*currentPointIn2D[1]; // columns = 0; ////m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); ////} ////else ////{ ////if ( columns != 0 ) ////{ ////currentImagePointIn3D += spacing[0]*right; ////} ////m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); ////} // ////if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) ////{ ////outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); ////} ////else if (currentImagePointIn3D == origin) ////{ ////Point3D temp; ////temp[0] = bottom[0]*spacing[0]*0.5; ////temp[1] = bottom[1]*spacing[1]*0.5; ////temp[2] = bottom[2]*spacing[2]*0.5; ////origin[0] += temp[0]; ////origin[1] += temp[1]; ////origin[2] += temp[2]; ////currentImagePointIn3D = origin; ////m_ImageGeometry3D->WorldToIndex(currentImagePointIn3D, outputIndex); ////if ( m_ImageGeometry3D->IsIndexInside( outputIndex )) ////{ ////outputImage->SetPixel( outputIndex, (TPixel2)inputIterator.Get() ); ////} ////} ////columns++; // ///****TEST ENDE****/ // ////* //// // Offset the world coordinate by one pixel to compensate for //// // index/world origin differences. //// Point3D offsetIndex; //// offsetIndex.Fill( 1 ); //// Point3D offsetWorld; //// offsetWorld.Fill( 0 ); //// m_PlaneGeometry->IndexToWorld( offsetIndex, offsetWorld ); //// // remove origin shift //// const Point3D origin = m_PlaneGeometry->GetOrigin(); //// offsetWorld[0] -= origin[0]; //// offsetWorld[1] -= origin[1]; //// offsetWorld[2] -= origin[2]; //// // offset world coordinate //// worldPointIn3D[ 0 ] += offsetWorld[0]; //// worldPointIn3D[ 1 ] += offsetWorld[1]; //// worldPointIn3D[ 2 ] += offsetWorld[2]; ////*/ // // Output index // // ////For the purpose of debug ////if( debugCounter%100 == 0) //////{ ////Point3D contIndex; ////m_ImageGeometry3D->WorldToIndex(worldPointIn3D,contIndex); ////overriderFile.precision(10); ////overriderFile<<"2D-Index: [ "<(inputIterator.Get() - outputPixel ) ); // oh oh, not good for // bigger values // ++inputIterator; ////++debugCounter; // //++diffIterator; // } // /*overriderFile.close(); // overriderFileIndex.close();*/ // geometryFile.close(); // ///* // typename VolumeImageType::RegionType sliceInVolumeRegion; // // sliceInVolumeRegion = outputImage->GetLargestPossibleRegion(); // sliceInVolumeRegion.SetSize( m_SliceDimension, 1 ); // just one slice // sliceInVolumeRegion.SetIndex( m_SliceDimension, m_SliceIndex ); // exactly this slice, please // // OutputSliceIteratorType outputIterator( outputImage, sliceInVolumeRegion ); // outputIterator.SetFirstDirection(m_Dimension0); // outputIterator.SetSecondDirection(m_Dimension1); // // // iterate over output slice (and over input slice simultaneously) // outputIterator.GoToBegin(); // while ( !outputIterator.IsAtEnd() ) // { // while ( !outputIterator.IsAtEndOfSlice() ) // { // while ( !outputIterator.IsAtEndOfLine() ) // { // diffIterator.Set( static_cast(inputIterator.Get() - outputIterator.Get()) ); // oh oh, not // good for bigger values // outputIterator.Set( (TPixel2) inputIterator.Get() ); // ++outputIterator; // ++inputIterator; // ++diffIterator; // } // outputIterator.NextLine(); // } // outputIterator.NextSlice(); // } // */ //} /* std::string mitk::OverwriteDirectedPlaneImageFilter::EventDescription( unsigned int sliceDimension, unsigned int sliceIndex, unsigned int timeStep ) { std::stringstream s; s << "Changed slice ("; switch (sliceDimension) { default: case 2: s << "T"; break; case 1: s << "C"; break; case 0: s << "S"; break; } s << " " << sliceIndex << " " << timeStep << ")"; return s.str(); } */ diff --git a/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp b/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp index 1c5dd9791d..8732bd4fb5 100644 --- a/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp +++ b/Modules/Segmentation/Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp @@ -1,141 +1,139 @@ /*=================================================================== 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 "mitkShapeBasedInterpolationAlgorithm.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include #include #include #include #include mitk::Image::Pointer mitk::ShapeBasedInterpolationAlgorithm::Interpolate( Image::ConstPointer lowerSlice, unsigned int lowerSliceIndex, Image::ConstPointer upperSlice, unsigned int upperSliceIndex, unsigned int requestedIndex, unsigned int /*sliceDimension*/, // commented variables are not used Image::Pointer resultImage, unsigned int /*timeStep*/, Image::ConstPointer /*referenceImage*/) { mitk::Image::Pointer lowerDistanceImage = mitk::Image::New(); AccessFixedDimensionByItk_1(lowerSlice, ComputeDistanceMap, 2, lowerDistanceImage); mitk::Image::Pointer upperDistanceImage = mitk::Image::New(); AccessFixedDimensionByItk_1(upperSlice, ComputeDistanceMap, 2, upperDistanceImage); // calculate where the current slice is in comparison to the lower and upper neighboring slices float ratio = (float)(requestedIndex - lowerSliceIndex) / (float)(upperSliceIndex - lowerSliceIndex); AccessFixedDimensionByItk_3( resultImage, InterpolateIntermediateSlice, 2, upperDistanceImage, lowerDistanceImage, ratio); return resultImage; } template void mitk::ShapeBasedInterpolationAlgorithm::ComputeDistanceMap(const itk::Image *binaryImage, mitk::Image::Pointer &result) { typedef itk::Image DistanceFilterInputImageType; typedef itk::FastChamferDistanceImageFilter DistanceFilterType; typedef itk::IsoContourDistanceImageFilter IsoContourType; typedef itk::InvertIntensityImageFilter InvertIntensityImageFilterType; typedef itk::SubtractImageFilter SubtractImageFilterType; typename DistanceFilterType::Pointer distanceFilter = DistanceFilterType::New(); typename DistanceFilterType::Pointer distanceFilterInverted = DistanceFilterType::New(); typename IsoContourType::Pointer isoContourFilter = IsoContourType::New(); typename IsoContourType::Pointer isoContourFilterInverted = IsoContourType::New(); typename InvertIntensityImageFilterType::Pointer invertFilter = InvertIntensityImageFilterType::New(); typename SubtractImageFilterType::Pointer subtractImageFilter = SubtractImageFilterType::New(); // arbitrary maximum distance int maximumDistance = 100; // this assumes the image contains only 1 and 0 invertFilter->SetInput(binaryImage); invertFilter->SetMaximum(1); // do the processing on the image and the inverted image to get inside and outside distance isoContourFilter->SetInput(binaryImage); isoContourFilter->SetFarValue(maximumDistance + 1); isoContourFilter->SetLevelSetValue(0.5); isoContourFilterInverted->SetInput(invertFilter->GetOutput()); isoContourFilterInverted->SetFarValue(maximumDistance + 1); isoContourFilterInverted->SetLevelSetValue(0.5); distanceFilter->SetInput(isoContourFilter->GetOutput()); distanceFilter->SetMaximumDistance(maximumDistance); distanceFilterInverted->SetInput(isoContourFilterInverted->GetOutput()); distanceFilterInverted->SetMaximumDistance(maximumDistance); // inside distance should be negative, outside distance positive subtractImageFilter->SetInput2(distanceFilter->GetOutput()); subtractImageFilter->SetInput1(distanceFilterInverted->GetOutput()); subtractImageFilter->Update(); result = mitk::GrabItkImageMemory(subtractImageFilter->GetOutput()); } template void mitk::ShapeBasedInterpolationAlgorithm::InterpolateIntermediateSlice(itk::Image *result, const mitk::Image::Pointer &lower, const mitk::Image::Pointer &upper, float ratio) { - typedef itk::Image ResultImageType; - typename DistanceFilterImageType::Pointer lowerITK = DistanceFilterImageType::New(); typename DistanceFilterImageType::Pointer upperITK = DistanceFilterImageType::New(); CastToItkImage(lower, lowerITK); CastToItkImage(upper, upperITK); itk::ImageRegionConstIteratorWithIndex lowerIter(lowerITK, lowerITK->GetLargestPossibleRegion()); lowerIter.GoToBegin(); if (!lowerITK->GetLargestPossibleRegion().IsInside(upperITK->GetLargestPossibleRegion()) || !lowerITK->GetLargestPossibleRegion().IsInside(result->GetLargestPossibleRegion())) { // TODO Exception etc. MITK_ERROR << "The regions of the slices for the 2D interpolation are not equally sized!"; return; } float weight[2] = {1.0f - ratio, ratio}; while (!lowerIter.IsAtEnd()) { typename DistanceFilterImageType::PixelType lowerPixelVal = lowerIter.Get(); typename DistanceFilterImageType::PixelType upperPixelVal = upperITK->GetPixel(lowerIter.GetIndex()); typename DistanceFilterImageType::PixelType intermediatePixelVal = (weight[0] * upperPixelVal + weight[1] * lowerPixelVal > 0 ? 0 : 1); result->SetPixel(lowerIter.GetIndex(), static_cast(intermediatePixelVal)); ++lowerIter; } } diff --git a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp index cff70f9384..11ca404825 100644 --- a/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp +++ b/Modules/Segmentation/Algorithms/mitkSurfaceStampImageFilter.cpp @@ -1,295 +1,294 @@ /*=================================================================== 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 #include #include #include #include #include #include #include mitk::SurfaceStampImageFilter::SurfaceStampImageFilter() - : m_MakeOutputBinary(false), m_OverwriteBackground(false), m_ForegroundValue(1.0), m_BackgroundValue(0.0) + : m_MakeOutputBinary(false), m_OverwriteBackground(false), m_BackgroundValue(0.0), m_ForegroundValue(1.0) { } mitk::SurfaceStampImageFilter::~SurfaceStampImageFilter() { } void mitk::SurfaceStampImageFilter::GenerateInputRequestedRegion() { mitk::Image *outputImage = this->GetOutput(); if ((outputImage->IsInitialized() == false)) return; GenerateTimeInInputRegion(outputImage, const_cast(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(), *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 transformFilter = vtkSmartPointer::New(); transformFilter->SetInputData(polydata); // transformFilter->ReleaseDataFlagOn(); vtkSmartPointer transform = vtkSmartPointer::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 (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 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(this->GetInput()); mitk::Image::Pointer outputImage = this->GetOutput(); typedef itk::Image BinaryImageType; BinaryImageType::Pointer itkInputImage; mitk::CastToItkImage(inputImage, itkInputImage); typedef itk::TriangleMeshToBinaryImageFilter 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 void mitk::SurfaceStampImageFilter::SurfaceStampProcessing(itk::Image *input, MeshType *mesh) { typedef itk::Image ImageType; typedef itk::Image BinaryImageType; typedef itk::TriangleMeshToBinaryImageFilter 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 BinaryIteratorType; typedef itk::ImageRegionConstIterator InputIteratorType; typedef itk::ImageRegionIterator 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(m_ForegroundValue); typename ImageType::PixelType bgValue = static_cast(m_BackgroundValue); while (!sourceIter.IsAtEnd()) { sourceValue = static_cast(sourceIter.Get()); inputValue = static_cast(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 2dd9a5a8ac..b7ace83a73 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 #include #include 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(); + auto 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 void mitk::SliceBasedInterpolationController::ScanSliceITKProcessing(const itk::Image *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 numberOfPixels; // number of pixels in the current slice that are equal to the active label unsigned int numberOfLabels = m_WorkingImage->GetNumberOfLabels(); numberOfPixels.resize(numberOfLabels); typedef itk::Image ImageType; typedef itk::ImageRegionConstIteratorWithIndex IteratorType; IteratorType iter(input, input->GetLargestPossibleRegion()); iter.GoToBegin(); typename IteratorType::IndexType index; while (!iter.IsAtEnd()) { index = iter.GetIndex(); int value = static_cast(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 void mitk::SliceBasedInterpolationController::ScanImageITKProcessing(itk::Image *input, unsigned int timeStep) { typedef itk::ImageSliceConstIteratorWithIndex> 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 numberOfPixels; // number of pixels per slice that are equal to the active label 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(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; 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; 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/mitkContourModelInteractor.cpp b/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp index 2abc699566..459aaa533e 100644 --- a/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp +++ b/Modules/Segmentation/Interactions/mitkContourModelInteractor.cpp @@ -1,168 +1,168 @@ /*=================================================================== 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 "mitkContourModelInteractor.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" #include #include mitk::ContourModelInteractor::ContourModelInteractor() { } void mitk::ContourModelInteractor::ConnectActionsAndFunctions() { CONNECT_CONDITION("checkisOverPoint", OnCheckPointClick); CONNECT_CONDITION("mouseMove", IsHovering); CONNECT_FUNCTION("movePoints", OnMovePoint); CONNECT_FUNCTION("deletePoint", OnDeletePoint); CONNECT_FUNCTION("finish", OnFinishEditing); } mitk::ContourModelInteractor::~ContourModelInteractor() { } bool mitk::ContourModelInteractor::OnCheckPointClick(const InteractionEvent *interactionEvent) { const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return false; int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast(this->GetDataNode()->GetData()); contour->Deselect(); mitk::Point3D click = positionEvent->GetPositionInWorld(); if (contour->SelectVertexAt(click, 1.5, timestep)) { contour->SetSelectedVertexAsControlPoint(); mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); m_lastMousePosition = click; mitk::Geometry3D *contourGeometry = dynamic_cast(contour->GetGeometry(timestep)); if (contourGeometry->IsInside(click)) { m_lastMousePosition = click; return true; } else return false; } else { return false; } return true; } -void mitk::ContourModelInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *interactionEvent) +void mitk::ContourModelInteractor::OnDeletePoint(StateMachineAction *, InteractionEvent *) { mitk::ContourModel *contour = dynamic_cast(this->GetDataNode()->GetData()); contour->RemoveVertex(contour->GetSelectedVertex()); } bool mitk::ContourModelInteractor::IsHovering(const InteractionEvent *interactionEvent) { const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return false; int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast(this->GetDataNode()->GetData()); mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); bool isHover = false; this->GetDataNode()->GetBoolProperty("contour.hovering", isHover, positionEvent->GetSender()); if (contour->IsNearContour(currentPosition, 1.5, timestep)) { if (isHover == false) { this->GetDataNode()->SetBoolProperty("contour.hovering", true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } } else { if (isHover == true) { this->GetDataNode()->SetBoolProperty("contour.hovering", false); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } } return false; } void mitk::ContourModelInteractor::OnMovePoint(StateMachineAction *, InteractionEvent *interactionEvent) { const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; mitk::ContourModel *contour = dynamic_cast(this->GetDataNode()->GetData()); mitk::Vector3D translation; mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); translation[0] = currentPosition[0] - this->m_lastMousePosition[0]; translation[1] = currentPosition[1] - this->m_lastMousePosition[1]; translation[2] = currentPosition[2] - this->m_lastMousePosition[2]; contour->ShiftSelectedVertex(translation); this->m_lastMousePosition = positionEvent->GetPositionInWorld(); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::ContourModelInteractor::OnMoveContour(StateMachineAction *, InteractionEvent *interactionEvent) { const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; int timestep = positionEvent->GetSender()->GetTimeStep(); mitk::ContourModel *contour = dynamic_cast(this->GetDataNode()->GetData()); mitk::Vector3D translation; mitk::Point3D currentPosition = positionEvent->GetPositionInWorld(); translation[0] = currentPosition[0] - this->m_lastMousePosition[0]; translation[1] = currentPosition[1] - this->m_lastMousePosition[1]; translation[2] = currentPosition[2] - this->m_lastMousePosition[2]; contour->ShiftContour(translation, timestep); this->m_lastMousePosition = positionEvent->GetPositionInWorld(); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::ContourModelInteractor::OnFinishEditing(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::ContourModel *contour = dynamic_cast(this->GetDataNode()->GetData()); contour->Deselect(); mitk::RenderingManager::GetInstance()->RequestUpdate(interactionEvent->GetSender()->GetRenderWindow()); } diff --git a/Modules/Segmentation/Interactions/mitkContourTool.cpp b/Modules/Segmentation/Interactions/mitkContourTool.cpp index e4f6152dae..251076d515 100644 --- a/Modules/Segmentation/Interactions/mitkContourTool.cpp +++ b/Modules/Segmentation/Interactions/mitkContourTool.cpp @@ -1,201 +1,201 @@ /*=================================================================== 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 "mitkContourTool.h" #include "mitkAbstractTransformGeometry.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkRenderingManager.h" //#include "mitkProperties.h" //#include "mitkPlanarCircle.h" #include "mitkLabelSetImage.h" #include "mitkInteractionEvent.h" #include "mitkStateMachineAction.h" mitk::ContourTool::ContourTool(int paintingPixelValue) : FeedbackContourTool("PressMoveReleaseWithCTRLInversion"), m_PaintingPixelValue(paintingPixelValue), m_CurrentLabelID(1) { } mitk::ContourTool::~ContourTool() { } void mitk::ContourTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION("Move", OnMouseMoved); CONNECT_FUNCTION("Release", OnMouseReleased); CONNECT_FUNCTION("InvertLogic", OnInvertLogic); } void mitk::ContourTool::Activated() { Superclass::Activated(); } void mitk::ContourTool::Deactivated() { Superclass::Deactivated(); } /** Just show the contour, insert the first point. */ void mitk::ContourTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel *contour = FeedbackContourTool::GetFeedbackContour(); // Clear feedback contour contour->Initialize(); // expand time bounds because our contour was initialized contour->Expand(timestep + 1); // draw as a closed contour contour->SetClosed(true, timestep); // add first mouse position mitk::Point3D point = positionEvent->GetPositionInWorld(); contour->AddVertex(point, timestep); FeedbackContourTool::SetFeedbackContourVisible(true); assert(positionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } /** Insert the point to the feedback contour. */ void mitk::ContourTool::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel *contour = FeedbackContourTool::GetFeedbackContour(); mitk::Point3D point = positionEvent->GetPositionInWorld(); contour->AddVertex(point, timestep); assert(positionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } /** Close the contour, project it to the image slice and fill it in 2D. */ void mitk::ContourTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent) { // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's // working image corresponds to that FeedbackContourTool::SetFeedbackContourVisible(false); mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); // const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return; assert(positionEvent->GetSender()->GetRenderWindow()); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) return; Image *image = dynamic_cast(workingNode->GetData()); const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); if (!image || !planeGeometry) return; // Check if it is a multilabel-image // If yes, get the new drawing color from it. // Otherwise nothing happens. mitk::LabelSetImage *labelSetImage = dynamic_cast(image); if (labelSetImage) { mitk::Label *label = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer()); m_CurrentLabelID = label->GetValue(); } else { m_CurrentLabelID = 1; } const AbstractTransformGeometry *abstractTransformGeometry( dynamic_cast(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); if (!image || abstractTransformGeometry) return; // 2. Slice is known, now we try to get it as a 2D image and project the contour into index coordinates of this slice Image::Pointer slice = SegTool2D::GetAffectedImageSliceAs2DImage(positionEvent, image); if (slice.IsNull()) { MITK_ERROR << "Unable to extract slice." << std::endl; return; } ContourModel *feedbackContour = FeedbackContourTool::GetFeedbackContour(); ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, true, false); // true: actually no idea why this is neccessary, but it works :-( if (projectedContour.IsNull()) return; int timestep = positionEvent->GetSender()->GetTimeStep(); // m_PaintingPixelValue only decides whether to paint or erase mitk::ContourModelUtils::FillContourInSlice( projectedContour, timestep, slice, image, (m_PaintingPixelValue * m_CurrentLabelID)); // this->WriteBackSegmentationResult(positionEvent, slice); SegTool2D::WriteBackSegmentationResult(positionEvent, slice); // 4. Make sure the result is drawn again --> is visible then. assert(positionEvent->GetSender()->GetRenderWindow()); } /** Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0. */ -void mitk::ContourTool::OnInvertLogic(StateMachineAction *, InteractionEvent *interactionEvent) +void mitk::ContourTool::OnInvertLogic(StateMachineAction *, InteractionEvent *) { // Inversion only for 0 and 1 as painting values if (m_PaintingPixelValue == 1) { m_PaintingPixelValue = 0; FeedbackContourTool::SetFeedbackContourColor(1.0, 0.0, 0.0); } else if (m_PaintingPixelValue == 0) { m_PaintingPixelValue = 1; FeedbackContourTool::SetFeedbackContourColorDefault(); } } diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp index 438a7aeec1..7a7abb080c 100644 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp @@ -1,484 +1,484 @@ /*=================================================================== 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 "mitkFastMarchingTool.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkInteractionConst.h" #include "mitkRenderingManager.h" #include "itkOrImageFilter.h" #include "mitkImageTimeSelector.h" // us #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, FastMarchingTool, "FastMarching2D tool"); } mitk::FastMarchingTool::FastMarchingTool() : FeedbackContourTool("PressMoveReleaseAndPointSetting"), m_NeedUpdate(true), m_CurrentTimeStep(0), m_PositionEvent(0), m_LowerThreshold(0), m_UpperThreshold(200), m_StoppingValue(100), m_Sigma(1.0), m_Alpha(-0.5), m_Beta(3.0) { } mitk::FastMarchingTool::~FastMarchingTool() { if (this->m_SmoothFilter.IsNotNull()) this->m_SmoothFilter->RemoveAllObservers(); if (this->m_SigmoidFilter.IsNotNull()) this->m_SigmoidFilter->RemoveAllObservers(); if (this->m_GradientMagnitudeFilter.IsNotNull()) this->m_GradientMagnitudeFilter->RemoveAllObservers(); if (this->m_FastMarchingFilter.IsNotNull()) this->m_FastMarchingFilter->RemoveAllObservers(); } void mitk::FastMarchingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("DeletePoint", OnDelete); } const char **mitk::FastMarchingTool::GetXPM() const { return nullptr; // mitkFastMarchingTool_xpm; } us::ModuleResource mitk::FastMarchingTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("FastMarching_48x48.png"); return resource; } us::ModuleResource mitk::FastMarchingTool::GetCursorIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("FastMarching_Cursor_32x32.png"); return resource; } const char *mitk::FastMarchingTool::GetName() const { return "2D Fast Marching"; } void mitk::FastMarchingTool::BuildITKPipeline() { m_ReferenceImageSliceAsITK = InternalImageType::New(); m_ReferenceImageSlice = GetAffectedReferenceSlice(m_PositionEvent); CastToItkImage(m_ReferenceImageSlice, m_ReferenceImageSliceAsITK); m_ProgressCommand = mitk::ToolCommand::New(); m_SmoothFilter = SmoothingFilterType::New(); m_SmoothFilter->SetInput(m_ReferenceImageSliceAsITK); m_SmoothFilter->SetTimeStep(0.05); m_SmoothFilter->SetNumberOfIterations(2); m_SmoothFilter->SetConductanceParameter(9.0); m_GradientMagnitudeFilter = GradientFilterType::New(); m_GradientMagnitudeFilter->SetSigma(m_Sigma); m_SigmoidFilter = SigmoidFilterType::New(); m_SigmoidFilter->SetAlpha(m_Alpha); m_SigmoidFilter->SetBeta(m_Beta); m_SigmoidFilter->SetOutputMinimum(0.0); m_SigmoidFilter->SetOutputMaximum(1.0); m_FastMarchingFilter = FastMarchingFilterType::New(); m_FastMarchingFilter->SetStoppingValue(m_StoppingValue); m_ThresholdFilter = ThresholdingFilterType::New(); m_ThresholdFilter->SetLowerThreshold(m_LowerThreshold); m_ThresholdFilter->SetUpperThreshold(m_UpperThreshold); m_ThresholdFilter->SetOutsideValue(0); m_ThresholdFilter->SetInsideValue(1.0); m_SeedContainer = NodeContainer::New(); m_SeedContainer->Initialize(); m_FastMarchingFilter->SetTrialPoints(m_SeedContainer); if (this->m_SmoothFilter.IsNotNull()) this->m_SmoothFilter->RemoveAllObservers(); if (this->m_SigmoidFilter.IsNotNull()) this->m_SigmoidFilter->RemoveAllObservers(); if (this->m_GradientMagnitudeFilter.IsNotNull()) this->m_GradientMagnitudeFilter->RemoveAllObservers(); if (this->m_FastMarchingFilter.IsNotNull()) this->m_FastMarchingFilter->RemoveAllObservers(); m_SmoothFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_GradientMagnitudeFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_SigmoidFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_FastMarchingFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); m_SmoothFilter->SetInput(m_ReferenceImageSliceAsITK); m_GradientMagnitudeFilter->SetInput(m_SmoothFilter->GetOutput()); m_SigmoidFilter->SetInput(m_GradientMagnitudeFilter->GetOutput()); m_FastMarchingFilter->SetInput(m_SigmoidFilter->GetOutput()); m_ThresholdFilter->SetInput(m_FastMarchingFilter->GetOutput()); m_ReferenceImageSliceAsITK = InternalImageType::New(); } void mitk::FastMarchingTool::SetUpperThreshold(double value) { if (m_UpperThreshold != value) { m_UpperThreshold = value / 10.0; m_ThresholdFilter->SetUpperThreshold(m_UpperThreshold); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetLowerThreshold(double value) { if (m_LowerThreshold != value) { m_LowerThreshold = value / 10.0; m_ThresholdFilter->SetLowerThreshold(m_LowerThreshold); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetBeta(double value) { if (m_Beta != value) { m_Beta = value; m_SigmoidFilter->SetBeta(m_Beta); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetSigma(double value) { if (m_Sigma != value) { m_Sigma = value; m_GradientMagnitudeFilter->SetSigma(m_Sigma); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetAlpha(double value) { if (m_Alpha != value) { m_Alpha = value; m_SigmoidFilter->SetAlpha(m_Alpha); m_NeedUpdate = true; } } void mitk::FastMarchingTool::SetStoppingValue(double value) { if (m_StoppingValue != value) { m_StoppingValue = value; m_FastMarchingFilter->SetStoppingValue(m_StoppingValue); m_NeedUpdate = true; } } void mitk::FastMarchingTool::Activated() { Superclass::Activated(); m_ResultImageNode = mitk::DataNode::New(); m_ResultImageNode->SetName("FastMarching_Preview"); m_ResultImageNode->SetBoolProperty("helper object", true); m_ResultImageNode->SetColor(0.0, 1.0, 0.0); m_ResultImageNode->SetVisibility(true); m_ToolManager->GetDataStorage()->Add(this->m_ResultImageNode, m_ToolManager->GetReferenceData(0)); m_SeedsAsPointSet = mitk::PointSet::New(); m_SeedsAsPointSetNode = mitk::DataNode::New(); m_SeedsAsPointSetNode->SetData(m_SeedsAsPointSet); m_SeedsAsPointSetNode->SetName("Seeds_Preview"); m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); m_SeedsAsPointSetNode->SetVisibility(true); m_ToolManager->GetDataStorage()->Add(this->m_SeedsAsPointSetNode, m_ToolManager->GetReferenceData(0)); this->Initialize(); } void mitk::FastMarchingTool::Deactivated() { m_ToolManager->GetDataStorage()->Remove(this->m_ResultImageNode); m_ToolManager->GetDataStorage()->Remove(this->m_SeedsAsPointSetNode); this->ClearSeeds(); m_ResultImageNode = nullptr; m_SeedsAsPointSetNode = nullptr; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); Superclass::Deactivated(); } void mitk::FastMarchingTool::Initialize() { m_ReferenceImage = dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData()); if (m_ReferenceImage->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = ImageTimeSelector::New(); timeSelector->SetInput(m_ReferenceImage); timeSelector->SetTimeNr(m_CurrentTimeStep); timeSelector->UpdateLargestPossibleRegion(); m_ReferenceImage = timeSelector->GetOutput(); } m_NeedUpdate = true; } void mitk::FastMarchingTool::ConfirmSegmentation() { // combine preview image with current working segmentation if (dynamic_cast(m_ResultImageNode->GetData())) { // logical or combination of preview and segmentation slice mitk::Image::Pointer workingImageSlice; mitk::Image::Pointer workingImage = dynamic_cast(this->m_ToolManager->GetWorkingData(0)->GetData()); workingImageSlice = GetAffectedImageSliceAs2DImage(m_WorkingPlane, workingImage, m_CurrentTimeStep); mitk::Image::Pointer segmentationResult = mitk::Image::New(); bool isDeprecatedUnsignedCharSegmentation = (workingImage->GetPixelType().GetComponentType() == itk::ImageIOBase::UCHAR); if (isDeprecatedUnsignedCharSegmentation) { typedef itk::Image OutputUCharImageType; OutputUCharImageType::Pointer workingImageSliceInITK = OutputUCharImageType::New(); CastToItkImage(workingImageSlice, workingImageSliceInITK); typedef itk::OrImageFilter OrImageFilterType; OrImageFilterType::Pointer orFilter = OrImageFilterType::New(); orFilter->SetInput1(m_ThresholdFilter->GetOutput()); orFilter->SetInput2(workingImageSliceInITK); orFilter->Update(); mitk::CastToMitkImage(orFilter->GetOutput(), segmentationResult); } else { OutputImageType::Pointer workingImageSliceInITK = OutputImageType::New(); CastToItkImage(workingImageSlice, workingImageSliceInITK); typedef itk::OrImageFilter OrImageFilterType; OrImageFilterType::Pointer orFilter = OrImageFilterType::New(); orFilter->SetInput(0, m_ThresholdFilter->GetOutput()); orFilter->SetInput(1, workingImageSliceInITK); orFilter->Update(); mitk::CastToMitkImage(orFilter->GetOutput(), segmentationResult); } segmentationResult->GetGeometry()->SetOrigin(workingImageSlice->GetGeometry()->GetOrigin()); segmentationResult->GetGeometry()->SetIndexToWorldTransform( workingImageSlice->GetGeometry()->GetIndexToWorldTransform()); // write to segmentation volume and hide preview image // again, current time step is not considered this->WriteBackSegmentationResult(m_WorkingPlane, segmentationResult, m_CurrentTimeStep); this->m_ResultImageNode->SetVisibility(false); this->ClearSeeds(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_ToolManager->ActivateTool(-1); } void mitk::FastMarchingTool::OnAddPoint(StateMachineAction *, InteractionEvent *interactionEvent) { // Add a new seed point for FastMarching algorithm mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); // const PositionEvent* p = dynamic_cast(stateEvent->GetEvent()); if (positionEvent == nullptr) return; if (m_PositionEvent.IsNotNull()) m_PositionEvent = nullptr; m_PositionEvent = InteractionPositionEvent::New(positionEvent->GetSender(), positionEvent->GetPointerPositionOnScreen()); // if click was on another renderwindow or slice then reset pipeline and preview if ((m_LastEventSender != m_PositionEvent->GetSender()) || (m_LastEventSlice != m_PositionEvent->GetSender()->GetSlice())) { this->BuildITKPipeline(); this->ClearSeeds(); } m_LastEventSender = m_PositionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_WorkingPlane = positionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone(); mitk::Point3D clickInIndex; m_ReferenceImageSlice->GetGeometry()->WorldToIndex(m_PositionEvent->GetPositionInWorld(), clickInIndex); itk::Index<2> seedPosition; seedPosition[0] = clickInIndex[0]; seedPosition[1] = clickInIndex[1]; NodeType node; const double seedValue = 0.0; node.SetValue(seedValue); node.SetIndex(seedPosition); this->m_SeedContainer->InsertElement(this->m_SeedContainer->Size(), node); m_FastMarchingFilter->Modified(); m_SeedsAsPointSet->InsertPoint(m_SeedsAsPointSet->GetSize(), m_PositionEvent->GetPositionInWorld()); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_NeedUpdate = true; this->Update(); m_ReadyMessage.Send(); } -void mitk::FastMarchingTool::OnDelete(StateMachineAction *, InteractionEvent *interactionEvent) +void mitk::FastMarchingTool::OnDelete(StateMachineAction *, InteractionEvent *) { // delete last seed point if (!(this->m_SeedContainer->empty())) { // delete last element of seeds container this->m_SeedContainer->pop_back(); m_FastMarchingFilter->Modified(); // delete last point in pointset - somehow ugly m_SeedsAsPointSet->GetPointSet()->GetPoints()->DeleteIndex(m_SeedsAsPointSet->GetSize() - 1); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_NeedUpdate = true; this->Update(); } } void mitk::FastMarchingTool::Update() { const unsigned int progress_steps = 20; // update FastMarching pipeline and show result if (m_NeedUpdate) { m_ProgressCommand->AddStepsToDo(progress_steps); CurrentlyBusy.Send(true); try { m_ThresholdFilter->Update(); } catch (itk::ExceptionObject &excep) { MITK_ERROR << "Exception caught: " << excep.GetDescription(); // progress by max step count, will force m_ProgressCommand->SetProgress(progress_steps); CurrentlyBusy.Send(false); std::string msg = excep.GetDescription(); ErrorMessage.Send(msg); return; } m_ProgressCommand->SetProgress(progress_steps); CurrentlyBusy.Send(false); // make output visible mitk::Image::Pointer result = mitk::Image::New(); CastToMitkImage(m_ThresholdFilter->GetOutput(), result); result->GetGeometry()->SetOrigin(m_ReferenceImageSlice->GetGeometry()->GetOrigin()); result->GetGeometry()->SetIndexToWorldTransform(m_ReferenceImageSlice->GetGeometry()->GetIndexToWorldTransform()); m_ResultImageNode->SetData(result); m_ResultImageNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void mitk::FastMarchingTool::ClearSeeds() { // clear seeds for FastMarching as well as the PointSet for visualization if (this->m_SeedContainer.IsNotNull()) this->m_SeedContainer->Initialize(); if (this->m_SeedsAsPointSet.IsNotNull()) { this->m_SeedsAsPointSet = mitk::PointSet::New(); this->m_SeedsAsPointSetNode->SetData(this->m_SeedsAsPointSet); m_SeedsAsPointSetNode->SetName("Seeds_Preview"); m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); m_SeedsAsPointSetNode->SetVisibility(true); } if (this->m_FastMarchingFilter.IsNotNull()) m_FastMarchingFilter->Modified(); this->m_NeedUpdate = true; } void mitk::FastMarchingTool::Reset() { // clear all seeds and preview empty result this->ClearSeeds(); m_ResultImageNode->SetVisibility(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::FastMarchingTool::SetCurrentTimeStep(int t) { if (m_CurrentTimeStep != t) { m_CurrentTimeStep = t; this->Initialize(); } } diff --git a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp index b2782339ac..e25bc3faab 100644 --- a/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPaintbrushTool.cpp @@ -1,607 +1,607 @@ /*=================================================================== 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 "mitkPaintbrushTool.h" #include "ipSegmentation.h" #include "mitkAbstractTransformGeometry.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkToolManager.h" #include "mitkContourModelUtils.h" #include "mitkLabelSetImage.h" #include "mitkLevelWindowProperty.h" #define ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : -(int)(0.5 - (a))) int mitk::PaintbrushTool::m_Size = 1; mitk::PaintbrushTool::PaintbrushTool(int paintingPixelValue) : FeedbackContourTool("PressMoveReleaseWithCTRLInversionAllMouseMoves"), m_PaintingPixelValue(paintingPixelValue), m_LastContourSize(0) // other than initial mitk::PaintbrushTool::m_Size (around l. 28) { m_MasterContour = ContourModel::New(); m_MasterContour->Initialize(); m_CurrentPlane = nullptr; m_WorkingNode = DataNode::New(); m_WorkingNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1))); m_WorkingNode->SetProperty("binary", mitk::BoolProperty::New(true)); } mitk::PaintbrushTool::~PaintbrushTool() { } void mitk::PaintbrushTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION("Move", OnPrimaryButtonPressedMoved); CONNECT_FUNCTION("MouseMove", OnMouseMoved); CONNECT_FUNCTION("Release", OnMouseReleased); CONNECT_FUNCTION("InvertLogic", OnInvertLogic); } void mitk::PaintbrushTool::Activated() { Superclass::Activated(); FeedbackContourTool::SetFeedbackContourVisible(true); SizeChanged.Send(m_Size); m_ToolManager->WorkingDataChanged += mitk::MessageDelegate(this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified); } void mitk::PaintbrushTool::Deactivated() { FeedbackContourTool::SetFeedbackContourVisible(false); if (m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); m_WorkingSlice = nullptr; m_CurrentPlane = nullptr; m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate(this, &mitk::PaintbrushTool::OnToolManagerWorkingDataModified); Superclass::Deactivated(); } void mitk::PaintbrushTool::SetSize(int value) { m_Size = value; } mitk::Point2D mitk::PaintbrushTool::upperLeft(mitk::Point2D p) { p[0] -= 0.5; p[1] += 0.5; return p; } void mitk::PaintbrushTool::UpdateContour(const InteractionPositionEvent *positionEvent) { // MITK_INFO<<"Update..."; // examine stateEvent and create a contour that matches the pixel mask that we are going to draw // mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent ); // const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return; // Get Spacing of current Slice // mitk::Vector3D vSpacing = m_WorkingSlice->GetSlicedGeometry()->GetPlaneGeometry(0)->GetSpacing(); // // Draw a contour in Square according to selected brush size // int radius = (m_Size) / 2; float fradius = static_cast(m_Size) / 2.0f; ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); // estimate center point of the brush ( relative to the pixel the mouse points on ) // -- left upper corner for even sizes, // -- midpoint for uneven sizes mitk::Point2D centerCorrection; centerCorrection.Fill(0); // even --> correction of [+0.5, +0.5] bool evenSize = ((m_Size % 2) == 0); if (evenSize) { centerCorrection[0] += 0.5; centerCorrection[1] += 0.5; } // we will compute the control points for the upper left quarter part of a circle contour std::vector quarterCycleUpperRight; std::vector quarterCycleLowerRight; std::vector quarterCycleLowerLeft; std::vector quarterCycleUpperLeft; mitk::Point2D curPoint; bool curPointIsInside = true; curPoint[0] = 0; curPoint[1] = radius; quarterCycleUpperRight.push_back(upperLeft(curPoint)); // to estimate if a pixel is inside the circle, we need to compare against the 'outer radius' // i.e. the distance from the midpoint [0,0] to the border of the pixel [0,radius] // const float outer_radius = static_cast(radius) + 0.5; while (curPoint[1] > 0) { // Move right until pixel is outside circle float curPointX_squared = 0.0f; float curPointY_squared = (curPoint[1] - centerCorrection[1]) * (curPoint[1] - centerCorrection[1]); while (curPointIsInside) { // increment posX and chec curPoint[0]++; curPointX_squared = (curPoint[0] - centerCorrection[0]) * (curPoint[0] - centerCorrection[0]); const float len = sqrt(curPointX_squared + curPointY_squared); if (len > fradius) { // found first Pixel in this horizontal line, that is outside the circle curPointIsInside = false; } } quarterCycleUpperRight.push_back(upperLeft(curPoint)); // Move down until pixel is inside circle while (!curPointIsInside) { // increment posX and chec curPoint[1]--; curPointY_squared = (curPoint[1] - centerCorrection[1]) * (curPoint[1] - centerCorrection[1]); const float len = sqrt(curPointX_squared + curPointY_squared); if (len <= fradius) { // found first Pixel in this horizontal line, that is outside the circle curPointIsInside = true; quarterCycleUpperRight.push_back(upperLeft(curPoint)); } // Quarter cycle is full, when curPoint y position is 0 if (curPoint[1] <= 0) break; } } // QuarterCycle is full! Now copy quarter cycle to other quarters. if (!evenSize) { std::vector::const_iterator it = quarterCycleUpperRight.begin(); while (it != quarterCycleUpperRight.end()) { mitk::Point2D p; p = *it; // the contour points in the lower right corner have same position but with negative y values p[1] *= -1; quarterCycleLowerRight.push_back(p); // the contour points in the lower left corner have same position // but with both x,y negative p[0] *= -1; quarterCycleLowerLeft.push_back(p); // the contour points in the upper left corner have same position // but with x negative p[1] *= -1; quarterCycleUpperLeft.push_back(p); it++; } } else { std::vector::const_iterator it = quarterCycleUpperRight.begin(); while (it != quarterCycleUpperRight.end()) { mitk::Point2D p, q; p = *it; q = p; // the contour points in the lower right corner have same position but with negative y values q[1] *= -1; // correct for moved offset if size even = the midpoint is not the midpoint of the current pixel // but its upper rigt corner q[1] += 1; quarterCycleLowerRight.push_back(q); q = p; // the contour points in the lower left corner have same position // but with both x,y negative q[1] = -1.0f * q[1] + 1; q[0] = -1.0f * q[0] + 1; quarterCycleLowerLeft.push_back(q); // the contour points in the upper left corner have same position // but with x negative q = p; q[0] *= -1; q[0] += 1; quarterCycleUpperLeft.push_back(q); it++; } } // fill contour with poins in right ordering, starting with the upperRight block mitk::Point3D tempPoint; for (unsigned int i = 0; i < quarterCycleUpperRight.size(); i++) { tempPoint[0] = quarterCycleUpperRight[i][0]; tempPoint[1] = quarterCycleUpperRight[i][1]; tempPoint[2] = 0; contourInImageIndexCoordinates->AddVertex(tempPoint); } // the lower right has to be parsed in reverse order for (int i = quarterCycleLowerRight.size() - 1; i >= 0; i--) { tempPoint[0] = quarterCycleLowerRight[i][0]; tempPoint[1] = quarterCycleLowerRight[i][1]; tempPoint[2] = 0; contourInImageIndexCoordinates->AddVertex(tempPoint); } for (unsigned int i = 0; i < quarterCycleLowerLeft.size(); i++) { tempPoint[0] = quarterCycleLowerLeft[i][0]; tempPoint[1] = quarterCycleLowerLeft[i][1]; tempPoint[2] = 0; contourInImageIndexCoordinates->AddVertex(tempPoint); } // the upper left also has to be parsed in reverse order for (int i = quarterCycleUpperLeft.size() - 1; i >= 0; i--) { tempPoint[0] = quarterCycleUpperLeft[i][0]; tempPoint[1] = quarterCycleUpperLeft[i][1]; tempPoint[2] = 0; contourInImageIndexCoordinates->AddVertex(tempPoint); } m_MasterContour = contourInImageIndexCoordinates; } /** Just show the contour, get one point as the central point and add surrounding points to the contour. */ void mitk::PaintbrushTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent) { if (m_WorkingSlice.IsNull()) return; mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; m_WorkingSlice->GetGeometry()->WorldToIndex(positionEvent->GetPositionInWorld(), m_LastPosition); // create new working node // a fresh node is needed to only display the actual drawing process for // the undo function if (m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); m_WorkingSlice = nullptr; m_CurrentPlane = nullptr; m_WorkingNode = DataNode::New(); m_WorkingNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1))); m_WorkingNode->SetProperty("binary", mitk::BoolProperty::New(true)); this->m_WorkingNode->SetVisibility(true); m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_MasterContour->SetClosed(true); this->MouseMoved(interactionEvent, true); } void mitk::PaintbrushTool::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) { MouseMoved(interactionEvent, false); } void mitk::PaintbrushTool::OnPrimaryButtonPressedMoved(StateMachineAction *, InteractionEvent *interactionEvent) { MouseMoved(interactionEvent, true); } /** Insert the point to the feedback contour,finish to build the contour and at the same time the painting function */ void mitk::PaintbrushTool::MouseMoved(mitk::InteractionEvent *interactionEvent, bool leftMouseButtonPressed) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); CheckIfCurrentSliceHasChanged(positionEvent); if (m_LastContourSize != m_Size) { UpdateContour(positionEvent); m_LastContourSize = m_Size; } Point3D worldCoordinates = positionEvent->GetPositionInWorld(); Point3D indexCoordinates; m_WorkingSlice->GetGeometry()->WorldToIndex(worldCoordinates, indexCoordinates); MITK_DEBUG << "Mouse at W " << worldCoordinates << std::endl; MITK_DEBUG << "Mouse at I " << indexCoordinates << std::endl; // round to nearest voxel center (abort if this hasn't changed) if (m_Size % 2 == 0) // even { indexCoordinates[0] = ROUND(indexCoordinates[0]); // /*+ 0.5*/) + 0.5; indexCoordinates[1] = ROUND(indexCoordinates[1]); // /*+ 0.5*/ ) + 0.5; } else // odd { indexCoordinates[0] = ROUND(indexCoordinates[0]); indexCoordinates[1] = ROUND(indexCoordinates[1]); } static Point3D lastPos; // uninitialized: if somebody finds out how this can be initialized in a one-liner, tell me if (fabs(indexCoordinates[0] - lastPos[0]) > mitk::eps || fabs(indexCoordinates[1] - lastPos[1]) > mitk::eps || fabs(indexCoordinates[2] - lastPos[2]) > mitk::eps || leftMouseButtonPressed) { lastPos = indexCoordinates; } else { MITK_DEBUG << "." << std::flush; return; } MITK_DEBUG << "Mouse at C " << indexCoordinates; int timestep = positionEvent->GetSender()->GetTimeStep(); ContourModel::Pointer contour = ContourModel::New(); contour->Expand(timestep + 1); contour->SetClosed(true, timestep); ContourModel::VertexIterator it = m_MasterContour->Begin(); ContourModel::VertexIterator end = m_MasterContour->End(); while (it != end) { Point3D point = (*it)->Coordinates; point[0] += indexCoordinates[0]; point[1] += indexCoordinates[1]; contour->AddVertex(point, timestep); it++; } if (leftMouseButtonPressed) { const double dist = indexCoordinates.EuclideanDistanceTo(m_LastPosition); const double radius = static_cast(m_Size) / 2.0; DataNode *workingNode(m_ToolManager->GetWorkingData(0)); Image::Pointer image = dynamic_cast(workingNode->GetData()); LabelSetImage *labelImage = dynamic_cast(image.GetPointer()); int activeColor = 1; if (labelImage) { activeColor = labelImage->GetActiveLabel(labelImage->GetActiveLayer())->GetValue(); } // m_PaintingPixelValue only decides whether to paint or erase mitk::ContourModelUtils::FillContourInSlice( contour, timestep, m_WorkingSlice, image, m_PaintingPixelValue * activeColor); m_WorkingNode->SetData(m_WorkingSlice); m_WorkingNode->Modified(); // if points are >= radius away draw rectangle to fill empty holes // in between the 2 points if (dist > radius) { const mitk::Point3D ¤tPos = indexCoordinates; mitk::Point3D direction; mitk::Point3D vertex; mitk::Point3D normal; direction[0] = indexCoordinates[0] - m_LastPosition[0]; direction[1] = indexCoordinates[1] - m_LastPosition[1]; direction[2] = indexCoordinates[2] - m_LastPosition[2]; direction[0] = direction.GetVnlVector().normalize()[0]; direction[1] = direction.GetVnlVector().normalize()[1]; direction[2] = direction.GetVnlVector().normalize()[2]; // 90 degrees rotation of direction normal[0] = -1.0 * direction[1]; normal[1] = direction[0]; contour->Clear(); // upper left corner vertex[0] = m_LastPosition[0] + (normal[0] * radius); vertex[1] = m_LastPosition[1] + (normal[1] * radius); contour->AddVertex(vertex); // upper right corner vertex[0] = currentPos[0] + (normal[0] * radius); vertex[1] = currentPos[1] + (normal[1] * radius); contour->AddVertex(vertex); // lower right corner vertex[0] = currentPos[0] - (normal[0] * radius); vertex[1] = currentPos[1] - (normal[1] * radius); contour->AddVertex(vertex); // lower left corner vertex[0] = m_LastPosition[0] - (normal[0] * radius); vertex[1] = m_LastPosition[1] - (normal[1] * radius); contour->AddVertex(vertex); mitk::ContourModelUtils::FillContourInSlice(contour, timestep, m_WorkingSlice, image, m_PaintingPixelValue * activeColor); m_WorkingNode->SetData(m_WorkingSlice); m_WorkingNode->Modified(); } } else { // switched from different renderwindow // no activate hover highlighting. Otherwise undo / redo wont work this->m_WorkingNode->SetVisibility(false); } m_LastPosition = indexCoordinates; // visualize contour ContourModel::Pointer displayContour = FeedbackContourTool::GetFeedbackContour(); displayContour->Clear(); ContourModel::Pointer tmp = FeedbackContourTool::BackProjectContourFrom2DSlice(m_WorkingSlice->GetGeometry(), /*displayContour*/ contour); // copy transformed contour into display contour it = tmp->Begin(); end = tmp->End(); while (it != end) { Point3D point = (*it)->Coordinates; displayContour->AddVertex(point, timestep); it++; } m_FeedbackContourNode->GetData()->Modified(); assert(positionEvent->GetSender()->GetRenderWindow()); RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::PaintbrushTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent) { // When mouse is released write segmentationresult back into image mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice->Clone()); // deactivate visibility of helper node m_WorkingNode->SetVisibility(false); RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } /** Called when the CTRL key is pressed. Will change the painting pixel value from 0 to 1 or from 1 to 0. */ -void mitk::PaintbrushTool::OnInvertLogic(StateMachineAction *, InteractionEvent *interactionEvent) +void mitk::PaintbrushTool::OnInvertLogic(StateMachineAction *, InteractionEvent *) { // Inversion only for 0 and 1 as painting values if (m_PaintingPixelValue == 1) { m_PaintingPixelValue = 0; FeedbackContourTool::SetFeedbackContourColor(1.0, 0.0, 0.0); } else if (m_PaintingPixelValue == 0) { m_PaintingPixelValue = 1; FeedbackContourTool::SetFeedbackContourColorDefault(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PaintbrushTool::CheckIfCurrentSliceHasChanged(const InteractionPositionEvent *event) { const PlaneGeometry *planeGeometry((event->GetSender()->GetCurrentWorldPlaneGeometry())); const AbstractTransformGeometry *abstractTransformGeometry( dynamic_cast(event->GetSender()->GetCurrentWorldPlaneGeometry())); DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) return; Image::Pointer image = dynamic_cast(workingNode->GetData()); if (!image || !planeGeometry || abstractTransformGeometry) return; if (m_CurrentPlane.IsNull() || m_WorkingSlice.IsNull()) { m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode->ReplaceProperty("color", workingNode->GetProperty("color")); m_WorkingNode->SetData(m_WorkingSlice); } else { bool isSameSlice(false); isSameSlice = mitk::MatrixEqualElementWise(planeGeometry->GetIndexToWorldTransform()->GetMatrix(), m_CurrentPlane->GetIndexToWorldTransform()->GetMatrix()); isSameSlice = mitk::Equal(planeGeometry->GetIndexToWorldTransform()->GetOffset(), m_CurrentPlane->GetIndexToWorldTransform()->GetOffset()); if (!isSameSlice) { m_ToolManager->GetDataStorage()->Remove(m_WorkingNode); m_CurrentPlane = nullptr; m_WorkingSlice = nullptr; m_WorkingNode = nullptr; m_CurrentPlane = const_cast(planeGeometry); m_WorkingSlice = SegTool2D::GetAffectedImageSliceAs2DImage(event, image)->Clone(); m_WorkingNode = mitk::DataNode::New(); m_WorkingNode->SetProperty("levelwindow", mitk::LevelWindowProperty::New(mitk::LevelWindow(0, 1))); m_WorkingNode->SetProperty("binary", mitk::BoolProperty::New(true)); m_WorkingNode->SetData(m_WorkingSlice); // So that the paintbrush contour vanished in the previous render window RenderingManager::GetInstance()->RequestUpdateAll(); } } if (!m_ToolManager->GetDataStorage()->Exists(m_WorkingNode)) { m_WorkingNode->SetProperty("outline binary", mitk::BoolProperty::New(true)); m_WorkingNode->SetProperty("color", workingNode->GetProperty("color")); m_WorkingNode->SetProperty("name", mitk::StringProperty::New("Paintbrush_Node")); m_WorkingNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_WorkingNode->SetProperty("opacity", mitk::FloatProperty::New(0.8)); m_WorkingNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_WorkingNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_ToolManager->GetDataStorage()->Add(m_WorkingNode); } } void mitk::PaintbrushTool::OnToolManagerWorkingDataModified() { // Here we simply set the current working slice to null. The next time the mouse is moved // within a renderwindow a new slice will be extracted from the new working data m_WorkingSlice = nullptr; } diff --git a/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp index f0d7b1dca6..26715cea7e 100644 --- a/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkRegionGrowingTool.cpp @@ -1,657 +1,655 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkRegionGrowingTool.h" #include "mitkApplicationCursor.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "mitkImageToContourModelFilter.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkRegionGrowingTool.xpm" #include "mitkRenderingManager.h" #include "mitkToolManager.h" #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkLabelSetImage.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" // us #include #include #include #include // ITK #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, RegionGrowingTool, "Region growing tool"); } #define ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : -(int)(0.5 - (a))) mitk::RegionGrowingTool::RegionGrowingTool() : FeedbackContourTool("PressMoveRelease"), m_SeedValue(0), m_ScreenYDifference(0), m_ScreenXDifference(0), m_VisibleWindow(0), m_MouseDistanceScaleFactor(0.5), m_FillFeedbackContour(true), m_ConnectedComponentValue(1) { } mitk::RegionGrowingTool::~RegionGrowingTool() { } void mitk::RegionGrowingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION("Move", OnMouseMoved); CONNECT_FUNCTION("Release", OnMouseReleased); } const char **mitk::RegionGrowingTool::GetXPM() const { return mitkRegionGrowingTool_xpm; } us::ModuleResource mitk::RegionGrowingTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("RegionGrowing_48x48.png"); return resource; } us::ModuleResource mitk::RegionGrowingTool::GetCursorIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("RegionGrowing_Cursor_32x32.png"); return resource; } const char *mitk::RegionGrowingTool::GetName() const { return "Region Growing"; } void mitk::RegionGrowingTool::Activated() { Superclass::Activated(); } void mitk::RegionGrowingTool::Deactivated() { Superclass::Deactivated(); } // Get the average pixel value of square/cube with radius=neighborhood around index template void mitk::RegionGrowingTool::GetNeighborhoodAverage(itk::Image *itkImage, itk::Index index, ScalarType *result, unsigned int neighborhood) { // maybe assert that image dimension is only 2 or 3? int neighborhoodInt = (int)neighborhood; TPixel averageValue(0); unsigned int numberOfPixels = (2 * neighborhood + 1) * (2 * neighborhood + 1); if (imageDimension == 3) { numberOfPixels *= (2 * neighborhood + 1); } MITK_DEBUG << "Getting neighborhood of " << numberOfPixels << " pixels around " << index; itk::Index currentIndex; for (int i = (0 - neighborhoodInt); i <= neighborhoodInt; ++i) { currentIndex[0] = index[0] + i; for (int j = (0 - neighborhoodInt); j <= neighborhoodInt; ++j) { currentIndex[1] = index[1] + j; if (imageDimension == 3) { for (int k = (0 - neighborhoodInt); k <= neighborhoodInt; ++k) { currentIndex[2] = index[2] + k; if (itkImage->GetLargestPossibleRegion().IsInside(currentIndex)) { averageValue += itkImage->GetPixel(currentIndex); } else { numberOfPixels -= 1; } } } else { if (itkImage->GetLargestPossibleRegion().IsInside(currentIndex)) { averageValue += itkImage->GetPixel(currentIndex); } else { numberOfPixels -= 1; } } } } *result = (ScalarType)averageValue; *result /= numberOfPixels; } // Check whether index lies inside a segmentation template void mitk::RegionGrowingTool::IsInsideSegmentation(itk::Image *itkImage, itk::Index index, bool *result) { if (itkImage->GetPixel(index) > 0) { *result = true; } else { *result = false; } } // Do the region growing (i.e. call an ITK filter that does it) template void mitk::RegionGrowingTool::StartRegionGrowing(itk::Image *inputImage, itk::Index seedIndex, std::array thresholds, mitk::Image::Pointer &outputImage) { MITK_DEBUG << "Starting region growing at index " << seedIndex << " with lower threshold " << thresholds[0] << " and upper threshold " << thresholds[1]; typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); // perform region growing in desired segmented region regionGrower->SetInput(inputImage); regionGrower->AddSeed(seedIndex); regionGrower->SetLower(thresholds[0]); regionGrower->SetUpper(thresholds[1]); try { regionGrower->Update(); } catch (...) { return; // Should we do something? } typename OutputImageType::Pointer resultImage = regionGrower->GetOutput(); // Smooth result: Every pixel is replaced by the majority of the neighborhood typedef itk::NeighborhoodIterator NeighborhoodIteratorType; typedef itk::ImageRegionIterator ImageIteratorType; typename NeighborhoodIteratorType::RadiusType radius; radius.Fill(2); // for now, maybe make this something the user can adjust in the preferences? typedef itk::ImageDuplicator< OutputImageType > DuplicatorType; typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(resultImage); duplicator->Update(); typename OutputImageType::Pointer resultDup = duplicator->GetOutput(); NeighborhoodIteratorType neighborhoodIterator(radius, resultDup, resultDup->GetRequestedRegion()); ImageIteratorType imageIterator(resultImage, resultImage->GetRequestedRegion()); for (neighborhoodIterator.GoToBegin(), imageIterator.GoToBegin(); !neighborhoodIterator.IsAtEnd(); ++neighborhoodIterator, ++imageIterator) { DefaultSegmentationDataType voteYes(0); DefaultSegmentationDataType voteNo(0); for (unsigned int i = 0; i < neighborhoodIterator.Size(); ++i) { if (neighborhoodIterator.GetPixel(i) > 0) { voteYes += 1; } else { voteNo += 1; } } if (voteYes > voteNo) { imageIterator.Set(1); } else { imageIterator.Set(0); } } if (resultImage.IsNull()) { MITK_DEBUG << "Region growing result is empty."; } // Can potentially have multiple regions, use connected component image filter to label disjunct regions typedef itk::ConnectedComponentImageFilter ConnectedComponentImageFilterType; typename ConnectedComponentImageFilterType::Pointer connectedComponentFilter = ConnectedComponentImageFilterType::New(); connectedComponentFilter->SetInput(resultImage); connectedComponentFilter->Update(); typename OutputImageType::Pointer resultImageCC = connectedComponentFilter->GetOutput(); m_ConnectedComponentValue = resultImageCC->GetPixel(seedIndex); outputImage = mitk::GrabItkImageMemory(resultImageCC); } void mitk::RegionGrowingTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; MITK_DEBUG << "OnMousePressed"; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); m_LastScreenPosition = positionEvent->GetPointerPositionOnScreen(); // ReferenceSlice is from the underlying image, WorkingSlice from the active segmentation (can be empty) m_ReferenceSlice = FeedbackContourTool::GetAffectedReferenceSlice(positionEvent); m_WorkingSlice = FeedbackContourTool::GetAffectedWorkingSlice(positionEvent); if (m_WorkingSlice.IsNotNull()) // can't do anything without a working slice (i.e. a possibly empty segmentation) { MITK_DEBUG << "OnMousePressed: got working slice"; // 2. Determine if the user clicked inside or outside of the segmentation/working slice (i.e. the whole volume) mitk::BaseGeometry::Pointer workingSliceGeometry; workingSliceGeometry = m_WorkingSlice->GetGeometry(); workingSliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), m_SeedPoint); itk::Index<2> indexInWorkingSlice2D; indexInWorkingSlice2D[0] = m_SeedPoint[0]; indexInWorkingSlice2D[1] = m_SeedPoint[1]; if (workingSliceGeometry->IsIndexInside(m_SeedPoint)) { MITK_DEBUG << "OnMousePressed: point " << positionEvent->GetPositionInWorld() << " (index coordinates " << m_SeedPoint << ") is inside working slice"; // 3. determine the pixel value under the last click to determine what to do bool inside(true); AccessFixedDimensionByItk_2(m_WorkingSlice, IsInsideSegmentation, 2, indexInWorkingSlice2D, &inside); m_PaintingPixelValue = inside ? 0 : 1; if (inside) { MITK_DEBUG << "Clicked inside segmentation"; // For now, we're doing nothing when the user clicks inside the segmentation. Behaviour can be implemented via // OnMousePressedInside() // When you do, be sure to remove the m_PaintingPixelValue check in OnMouseMoved() and OnMouseReleased() return; } else { MITK_DEBUG << "Clicked outside of segmentation"; OnMousePressedOutside(nullptr, interactionEvent); } } } } // Use this to implement a behaviour for when the user clicks inside a segmentation (for example remove something) // Old IpPic code is kept as comment for reference void mitk::RegionGrowingTool::OnMousePressedInside() { // mitk::InteractionPositionEvent* positionEvent = dynamic_cast( interactionEvent // ); // //const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); // checked in // OnMousePressed // // 3.1.1. Create a skeletonization of the segmentation and try to find a nice cut // // apply the skeletonization-and-cut algorithm // // generate contour to remove // // set m_ReferenceSlice = nullptr so nothing will happen during mouse move // // remember to fill the contour with 0 in mouserelease // mitkIpPicDescriptor* segmentationHistory = ipMITKSegmentationCreateGrowerHistory( workingPicSlice, // m_LastWorkingSeed, nullptr ); // free again // if (segmentationHistory) // { // tCutResult cutContour = ipMITKSegmentationGetCutPoints( workingPicSlice, segmentationHistory, // initialWorkingOffset ); // tCutResult is a ipSegmentation type // mitkIpPicFree( segmentationHistory ); // if (cutContour.cutIt) // { // int timestep = positionEvent->GetSender()->GetTimeStep(); // // 3.1.2 copy point from float* to mitk::Contour // ContourModel::Pointer contourInImageIndexCoordinates = ContourModel::New(); // contourInImageIndexCoordinates->Expand(timestep + 1); // contourInImageIndexCoordinates->SetClosed(true, timestep); // Point3D newPoint; // for (int index = 0; index < cutContour.deleteSize; ++index) // { // newPoint[0] = cutContour.deleteCurve[ 2 * index + 0 ] - 0.5;//correction is needed because the // output of the algorithm is center based // newPoint[1] = cutContour.deleteCurve[ 2 * index + 1 ] - 0.5;//and we want our contour displayed // corner based. // newPoint[2] = 0.0; // contourInImageIndexCoordinates->AddVertex( newPoint, timestep ); // } // free(cutContour.traceline); // free(cutContour.deleteCurve); // perhaps visualize this for fun? // free(cutContour.onGradient); // ContourModel::Pointer contourInWorldCoordinates = FeedbackContourTool::BackProjectContourFrom2DSlice( // m_WorkingSlice->GetGeometry(), contourInImageIndexCoordinates, true ); // true: sub 0.5 for // ipSegmentation correction // FeedbackContourTool::SetFeedbackContour( contourInWorldCoordinates ); // FeedbackContourTool::SetFeedbackContourVisible(true); // mitk::RenderingManager::GetInstance()->RequestUpdate( positionEvent->GetSender()->GetRenderWindow() ); // m_FillFeedbackContour = true; // } // else // { // m_FillFeedbackContour = false; // } // } // else // { // m_FillFeedbackContour = false; // } // m_ReferenceSlice = nullptr; // return true; } void mitk::RegionGrowingTool::OnMousePressedOutside(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (positionEvent) { // Get geometry and indices mitk::BaseGeometry::Pointer workingSliceGeometry; workingSliceGeometry = m_WorkingSlice->GetGeometry(); itk::Index<2> indexInWorkingSlice2D; indexInWorkingSlice2D[0] = m_SeedPoint[0]; indexInWorkingSlice2D[1] = m_SeedPoint[1]; mitk::BaseGeometry::Pointer referenceSliceGeometry; referenceSliceGeometry = m_ReferenceSlice->GetGeometry(); itk::Index<3> indexInReferenceSlice; itk::Index<2> indexInReferenceSlice2D; referenceSliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), indexInReferenceSlice); indexInReferenceSlice2D[0] = indexInReferenceSlice[0]; indexInReferenceSlice2D[1] = indexInReferenceSlice[1]; // Get seed neighborhood ScalarType averageValue(0); AccessFixedDimensionByItk_3(m_ReferenceSlice, GetNeighborhoodAverage, 2, indexInReferenceSlice2D, &averageValue, 1); m_SeedValue = averageValue; MITK_DEBUG << "Seed value is " << m_SeedValue; // Get level window settings LevelWindow lw(0, 500); // default window 0 to 500, can we do something smarter here? m_ToolManager->GetReferenceData(0)->GetLevelWindow( lw); // will fill lw if levelwindow property is present, otherwise won't touch it. ScalarType currentVisibleWindow = lw.GetWindow(); MITK_DEBUG << "Level window width is " << currentVisibleWindow; m_InitialThresholds[0] = m_SeedValue - currentVisibleWindow / 20.0; // 20 is arbitrary (though works reasonably // well), is there a better alternative (maybe // option in preferences)? m_InitialThresholds[1] = m_SeedValue + currentVisibleWindow / 20.0; m_Thresholds[0] = m_InitialThresholds[0]; m_Thresholds[1] = m_InitialThresholds[1]; // Perform region growing mitk::Image::Pointer resultImage = mitk::Image::New(); AccessFixedDimensionByItk_3( m_ReferenceSlice, StartRegionGrowing, 2, indexInWorkingSlice2D, m_Thresholds, resultImage); resultImage->SetGeometry(workingSliceGeometry); // Extract contour if (resultImage.IsNotNull() && m_ConnectedComponentValue >= 1) { float isoOffset = 0.33; mitk::ImageToContourModelFilter::Pointer contourExtractor = mitk::ImageToContourModelFilter::New(); contourExtractor->SetInput(resultImage); contourExtractor->SetContourValue(m_ConnectedComponentValue - isoOffset); contourExtractor->Update(); ContourModel::Pointer resultContour = ContourModel::New(); resultContour = contourExtractor->GetOutput(); // Show contour if (resultContour.IsNotNull()) { ContourModel::Pointer resultContourWorld = FeedbackContourTool::BackProjectContourFrom2DSlice( workingSliceGeometry, FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, resultContour)); // this is not a beautiful solution, just one that works, check T22412 for details int timestep = positionEvent->GetSender()->GetTimeStep(); if (0 != timestep) { int size = resultContourWorld->GetNumberOfVertices(0); auto resultContourTimeWorld = mitk::ContourModel::New(); resultContourTimeWorld->Expand(timestep + 1); for (int loop = 0; loop < size; ++loop) { resultContourTimeWorld->AddVertex(resultContourWorld->GetVertexAt(loop, 0), timestep); } FeedbackContourTool::SetFeedbackContour(resultContourTimeWorld); } else { FeedbackContourTool::SetFeedbackContour(resultContourWorld); } FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(m_LastEventSender->GetRenderWindow()); } } } } void mitk::RegionGrowingTool::OnMouseMoved(StateMachineAction *, InteractionEvent *interactionEvent) { // Until OnMousePressedInside() implements a behaviour, we're just returning here whenever m_PaintingPixelValue is 0, // i.e. when the user clicked inside the segmentation if (m_PaintingPixelValue == 0) { return; } mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (m_ReferenceSlice.IsNotNull() && positionEvent) { // Get geometry and indices mitk::BaseGeometry::Pointer workingSliceGeometry; workingSliceGeometry = m_WorkingSlice->GetGeometry(); itk::Index<2> indexInWorkingSlice2D; indexInWorkingSlice2D[0] = m_SeedPoint[0]; indexInWorkingSlice2D[1] = m_SeedPoint[1]; m_ScreenYDifference += positionEvent->GetPointerPositionOnScreen()[1] - m_LastScreenPosition[1]; m_ScreenXDifference += positionEvent->GetPointerPositionOnScreen()[0] - m_LastScreenPosition[0]; m_LastScreenPosition = positionEvent->GetPointerPositionOnScreen(); // Moving the mouse up and down adjusts the width of the threshold window, moving it left and right shifts the // threshold window m_Thresholds[0] = std::min( m_SeedValue, m_InitialThresholds[0] - (m_ScreenYDifference - m_ScreenXDifference) * m_MouseDistanceScaleFactor); m_Thresholds[1] = std::max( m_SeedValue, m_InitialThresholds[1] + (m_ScreenYDifference + m_ScreenXDifference) * m_MouseDistanceScaleFactor); MITK_DEBUG << "Screen difference X: " << m_ScreenXDifference; // Perform region growing again and show the result mitk::Image::Pointer resultImage = mitk::Image::New(); AccessFixedDimensionByItk_3( m_ReferenceSlice, StartRegionGrowing, 2, indexInWorkingSlice2D, m_Thresholds, resultImage); resultImage->SetGeometry(workingSliceGeometry); // Update the contour if (resultImage.IsNotNull() && m_ConnectedComponentValue >= 1) { float isoOffset = 0.33; mitk::ImageToContourModelFilter::Pointer contourExtractor = mitk::ImageToContourModelFilter::New(); contourExtractor->SetInput(resultImage); contourExtractor->SetContourValue(m_ConnectedComponentValue - isoOffset); contourExtractor->Update(); ContourModel::Pointer resultContour = ContourModel::New(); resultContour = contourExtractor->GetOutput(); // Show contour if (resultContour.IsNotNull()) { ContourModel::Pointer resultContourWorld = FeedbackContourTool::BackProjectContourFrom2DSlice( workingSliceGeometry, FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, resultContour)); // this is not a beautiful solution, just one that works, check T22412 for details int timestep = positionEvent->GetSender()->GetTimeStep(); if (0 != timestep) { int size = resultContourWorld->GetNumberOfVertices(0); auto resultContourTimeWorld = mitk::ContourModel::New(); resultContourTimeWorld->Expand(timestep + 1); for (int loop = 0; loop < size; ++loop) { resultContourTimeWorld->AddVertex(resultContourWorld->GetVertexAt(loop, 0), timestep); } FeedbackContourTool::SetFeedbackContour(resultContourTimeWorld); } else { FeedbackContourTool::SetFeedbackContour(resultContourWorld); } FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->ForceImmediateUpdate(positionEvent->GetSender()->GetRenderWindow()); } } } } void mitk::RegionGrowingTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent) { // Until OnMousePressedInside() implements a behaviour, we're just returning here whenever m_PaintingPixelValue is 0, // i.e. when the user clicked inside the segmentation if (m_PaintingPixelValue == 0) { return; } mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (m_WorkingSlice.IsNotNull() && m_FillFeedbackContour && positionEvent) { // Project contour into working slice ContourModel *feedbackContour(FeedbackContourTool::GetFeedbackContour()); ContourModel::Pointer projectedContour; // this is not a beautiful solution, just one that works, check T22412 for details int timestep = positionEvent->GetSender()->GetTimeStep(); if (0 != timestep) { int size = feedbackContour->GetNumberOfVertices(timestep); auto feedbackContourTime = mitk::ContourModel::New(); feedbackContourTime->Expand(timestep + 1); for (int loop = 0; loop < size; ++loop) { feedbackContourTime->AddVertex(feedbackContour->GetVertexAt(loop, timestep), 0); } projectedContour = FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, feedbackContourTime, false, false); } else { projectedContour = FeedbackContourTool::ProjectContourTo2DSlice(m_WorkingSlice, feedbackContour, false, false); } // If there is a projected contour, fill it if (projectedContour.IsNotNull()) { // Get working data to pass to following method so we don't overwrite locked labels in a LabelSetImage mitk::DataNode *workingNode(m_ToolManager->GetWorkingData(0)); - mitk::LabelSetImage *labelImage; - if (workingNode) - { - labelImage = dynamic_cast(workingNode->GetData()); - } + mitk::LabelSetImage *labelImage = workingNode != nullptr + ? dynamic_cast(workingNode->GetData()) + : nullptr; MITK_DEBUG << "Filling Segmentation"; - if (labelImage) + if (labelImage != nullptr) { // m_PaintingPixelValue only decides whether to paint or not // For LabelSetImages we want to paint with the active label value auto activeLabel = labelImage->GetActiveLabel(labelImage->GetActiveLayer())->GetValue(); mitk::ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingSlice, labelImage, m_PaintingPixelValue * activeLabel); } else { mitk::ContourModelUtils::FillContourInSlice(projectedContour, 0, m_WorkingSlice, m_WorkingSlice, m_PaintingPixelValue); } this->WriteBackSegmentationResult(positionEvent, m_WorkingSlice); FeedbackContourTool::SetFeedbackContourVisible(false); } m_ScreenYDifference = 0; m_ScreenXDifference = 0; } } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index 2b65547205..e78f90115b 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,629 +1,629 @@ /*=================================================================== 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 "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkDataStorage.h" #include "mitkPlaneGeometry.h" #include "mitkExtractDirectedPlaneImageFilter.h" #include "mitkExtractImageFilter.h" // Include of the new ImageExtractor #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkMorphologicalOperations.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkPlanarCircle.h" #include "usGetModuleContext.h" // Includes for 3DSurfaceInterpolation #include "mitkImageTimeSelector.h" #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" // includes for resling and overwriting #include #include #include #include #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include #include "mitkAbstractTransformGeometry.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageToItk.h" #include "mitkLabelSetImage.h" #define ROUND(a) ((a) > 0 ? (int)((a) + 0.5) : -(int)(0.5 - (a))) bool mitk::SegTool2D::m_SurfaceInterpolationEnabled = true; mitk::SegTool2D::SegTool2D(const char *type) : Tool(type), m_LastEventSender(nullptr), m_LastEventSlice(0), m_Contourmarkername("Position"), m_ShowMarkerNodes(false) { Tool::m_EventConfig = "DisplayConfigMITKNoCrosshair.xml"; } mitk::SegTool2D::~SegTool2D() { } bool mitk::SegTool2D::FilterEvents(InteractionEvent *interactionEvent, DataNode *) { const InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); bool isValidEvent = (positionEvent && // Only events of type mitk::InteractionPositionEvent interactionEvent->GetSender()->GetMapperID() == BaseRenderer::Standard2D // Only events from the 2D renderwindows ); return isValidEvent; } bool mitk::SegTool2D::DetermineAffectedImageSlice(const Image *image, const PlaneGeometry *plane, int &affectedDimension, int &affectedSlice) { assert(image); assert(plane); // compare normal of plane to the three axis vectors of the image Vector3D normal = plane->GetNormal(); Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0); Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1); Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2); normal.Normalize(); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); imageNormal0.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal0.GetVnlVector())); imageNormal1.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal1.GetVnlVector())); imageNormal2.SetVnlVector(vnl_cross_3d(normal.GetVnlVector(), imageNormal2.GetVnlVector())); double eps(0.00001); // axial if (imageNormal2.GetNorm() <= eps) { affectedDimension = 2; } // sagittal else if (imageNormal1.GetNorm() <= eps) { affectedDimension = 1; } // frontal else if (imageNormal0.GetNorm() <= eps) { affectedDimension = 0; } else { affectedDimension = -1; // no idea return false; } // determine slice number in image BaseGeometry *imageGeometry = image->GetGeometry(0); Point3D testPoint = imageGeometry->GetCenter(); Point3D projectedPoint; plane->Project(testPoint, projectedPoint); Point3D indexPoint; imageGeometry->WorldToIndex(projectedPoint, indexPoint); affectedSlice = ROUND(indexPoint[affectedDimension]); MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice " << affectedSlice; // check if this index is still within the image if (affectedSlice < 0 || affectedSlice >= static_cast(image->GetDimension(affectedDimension))) return false; return true; } void mitk::SegTool2D::UpdateSurfaceInterpolation(const Image *slice, const Image *workingImage, const PlaneGeometry *plane, bool detectIntersection) { if (!m_SurfaceInterpolationEnabled) return; ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); mitk::Surface::Pointer contour; if (detectIntersection) { // Test whether there is something to extract or whether the slice just contains intersections of others mitk::Image::Pointer slice2 = slice->Clone(); mitk::MorphologicalOperations::Erode(slice2, 2, mitk::MorphologicalOperations::Ball); contourExtractor->SetInput(slice2); contourExtractor->Update(); contour = contourExtractor->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() == 0) { // Remove contour! mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo; contourInfo.contourNormal = plane->GetNormal(); contourInfo.contourPoint = plane->GetOrigin(); mitk::SurfaceInterpolationController::GetInstance()->RemoveContour(contourInfo); return; } } contourExtractor->SetInput(slice); contourExtractor->Update(); contour = contourExtractor->GetOutput(); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(workingImage); timeSelector->SetTimeNr(0); timeSelector->SetChannelNr(0); timeSelector->Update(); Image::Pointer dimRefImg = timeSelector->GetOutput(); if (contour->GetVtkPolyData()->GetNumberOfPoints() != 0 && dimRefImg->GetDimension() == 3) { mitk::SurfaceInterpolationController::GetInstance()->AddNewContour(contour); contour->DisconnectPipeline(); } else { // Remove contour! mitk::SurfaceInterpolationController::ContourPositionInformation contourInfo; contourInfo.contourNormal = plane->GetNormal(); contourInfo.contourPoint = plane->GetOrigin(); mitk::SurfaceInterpolationController::GetInstance()->RemoveContour(contourInfo); } } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const InteractionPositionEvent *positionEvent, const Image *image, unsigned int component /*= 0*/) { if (!positionEvent) { return nullptr; } assert(positionEvent->GetSender()); // sure, right? unsigned int timeStep = positionEvent->GetSender()->GetTimeStep(image); // get the timestep of the visible part (time-wise) of the image return GetAffectedImageSliceAs2DImage(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry(), image, timeStep, component); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PlaneGeometry *planeGeometry, const Image *image, unsigned int timeStep, unsigned int component /*= 0*/) { if (!image || !planeGeometry) { return nullptr; } // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // set to false to extract a slice reslice->SetOverwriteMode(false); reslice->Modified(); // use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput(image); extractor->SetTimeStep(timeStep); extractor->SetWorldGeometry(planeGeometry); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry(image->GetTimeGeometry()->GetGeometryForTimeStep(timeStep)); // additionally extract the given component // default is 0; the extractor checks for multi-component images extractor->SetComponent(component); extractor->Modified(); extractor->Update(); Image::Pointer slice = extractor->GetOutput(); return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const InteractionPositionEvent *positionEvent) { DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) { return nullptr; } Image *workingImage = dynamic_cast(workingNode->GetData()); if (!workingImage) { return nullptr; } return GetAffectedImageSliceAs2DImage(positionEvent, workingImage); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const InteractionPositionEvent *positionEvent) { DataNode *referenceNode(m_ToolManager->GetReferenceData(0)); if (!referenceNode) { return nullptr; } Image *referenceImage = dynamic_cast(referenceNode->GetData()); if (!referenceImage) { return nullptr; } int displayedComponent = 0; if (referenceNode->GetIntProperty("Image.Displayed Component", displayedComponent)) { // found the displayed component return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage, displayedComponent); } else { return GetAffectedImageSliceAs2DImage(positionEvent, referenceImage); } } void mitk::SegTool2D::WriteBackSegmentationResult(const InteractionPositionEvent *positionEvent, Image *slice) { if (!positionEvent) return; const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); const AbstractTransformGeometry *abstractTransformGeometry( dynamic_cast(positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); if (planeGeometry && slice && !abstractTransformGeometry) { DataNode *workingNode(m_ToolManager->GetWorkingData(0)); Image *image = dynamic_cast(workingNode->GetData()); unsigned int timeStep = positionEvent->GetSender()->GetTimeStep(image); this->WriteBackSegmentationResult(planeGeometry, slice, timeStep); } } void mitk::SegTool2D::WriteBackSegmentationResult(const PlaneGeometry *planeGeometry, Image *slice, unsigned int timeStep) { if (!planeGeometry || !slice) return; SliceInformation sliceInfo(slice, const_cast(planeGeometry), timeStep); this->WriteSliceToVolume(sliceInfo); DataNode *workingNode(m_ToolManager->GetWorkingData(0)); Image *image = dynamic_cast(workingNode->GetData()); this->UpdateSurfaceInterpolation(slice, image, planeGeometry, false); if (m_SurfaceInterpolationEnabled) this->AddContourmarker(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::WriteBackSegmentationResult(std::vector sliceList, bool writeSliceToVolume) { std::vector contourList; contourList.reserve(sliceList.size()); ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); DataNode *workingNode(m_ToolManager->GetWorkingData(0)); Image *image = dynamic_cast(workingNode->GetData()); mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(image); timeSelector->SetTimeNr(0); timeSelector->SetChannelNr(0); timeSelector->Update(); Image::Pointer dimRefImg = timeSelector->GetOutput(); for (unsigned int i = 0; i < sliceList.size(); ++i) { SliceInformation currentSliceInfo = sliceList.at(i); if (writeSliceToVolume) this->WriteSliceToVolume(currentSliceInfo); if (m_SurfaceInterpolationEnabled && dimRefImg->GetDimension() == 3) { currentSliceInfo.slice->DisconnectPipeline(); contourExtractor->SetInput(currentSliceInfo.slice); contourExtractor->Update(); mitk::Surface::Pointer contour = contourExtractor->GetOutput(); contour->DisconnectPipeline(); contourList.push_back(contour); } } mitk::SurfaceInterpolationController::GetInstance()->AddNewContours(contourList); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::WriteSliceToVolume(mitk::SegTool2D::SliceInformation sliceInfo) { DataNode *workingNode(m_ToolManager->GetWorkingData(0)); Image *image = dynamic_cast(workingNode->GetData()); /*============= BEGIN undo/redo feature block ========================*/ // Create undo operation by caching the not yet modified slices mitk::Image::Pointer originalSlice = GetAffectedImageSliceAs2DImage(sliceInfo.plane, image, sliceInfo.timestep); DiffSliceOperation *undoOperation = new DiffSliceOperation(const_cast(image), originalSlice, dynamic_cast(originalSlice->GetGeometry()), sliceInfo.timestep, sliceInfo.plane); /*============= END undo/redo feature block ========================*/ // Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk // reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); // Set the slice as 'input' reslice->SetInputSlice(sliceInfo.slice->GetVtkImageData()); // set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput(image); extractor->SetTimeStep(sliceInfo.timestep); extractor->SetWorldGeometry(sliceInfo.plane); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry(image->GetGeometry(sliceInfo.timestep)); extractor->Modified(); extractor->Update(); // the image was modified within the pipeline, but not marked so image->Modified(); image->GetVtkImageData()->Modified(); /*============= BEGIN undo/redo feature block ========================*/ // specify the undo operation with the edited slice DiffSliceOperation *doOperation = new DiffSliceOperation(image, extractor->GetOutput(), dynamic_cast(sliceInfo.slice->GetGeometry()), sliceInfo.timestep, sliceInfo.plane); // create an operation event for the undo stack OperationEvent *undoStackItem = new OperationEvent(DiffSliceOperationApplier::GetInstance(), doOperation, undoOperation, "Segmentation"); // add it to the undo controller UndoStackItem::IncCurrObjectEventId(); UndoStackItem::IncCurrGroupEventId(); UndoController::GetCurrentUndoModel()->SetOperationEvent(undoStackItem); // clear the pointers as the operation are stored in the undocontroller and also deleted from there undoOperation = nullptr; doOperation = nullptr; /*============= END undo/redo feature block ========================*/ } void mitk::SegTool2D::SetShowMarkerNodes(bool status) { m_ShowMarkerNodes = status; } void mitk::SegTool2D::SetEnable3DInterpolation(bool enabled) { m_SurfaceInterpolationEnabled = enabled; } int mitk::SegTool2D::AddContourmarker() { if (m_LastEventSender == nullptr) return -1; us::ServiceReference serviceRef = us::GetModuleContext()->GetServiceReference(); PlanePositionManagerService *service = us::GetModuleContext()->GetService(serviceRef); unsigned int slicePosition = m_LastEventSender->GetSliceNavigationController()->GetSlice()->GetPos(); // the first geometry is needed otherwise restoring the position is not working const mitk::PlaneGeometry *plane = dynamic_cast(dynamic_cast( m_LastEventSender->GetSliceNavigationController()->GetCurrentGeometry3D()) ->GetPlaneGeometry(0)); unsigned int size = service->GetNumberOfPlanePositions(); unsigned int id = service->AddNewPlanePosition(plane, slicePosition); mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); mitk::Point2D p1; plane->Map(plane->GetCenter(), p1); mitk::Point2D p2 = p1; p2[0] -= plane->GetSpacing()[0]; p2[1] -= plane->GetSpacing()[1]; contourMarker->PlaceFigure(p1); contourMarker->SetCurrentControlPoint(p1); contourMarker->SetPlaneGeometry(const_cast(plane)); std::stringstream markerStream; mitk::DataNode *workingNode(m_ToolManager->GetWorkingData(0)); markerStream << m_Contourmarkername; markerStream << " "; markerStream << id + 1; DataNode::Pointer rotatedContourNode = DataNode::New(); rotatedContourNode->SetData(contourMarker); rotatedContourNode->SetProperty("name", StringProperty::New(markerStream.str())); rotatedContourNode->SetProperty("isContourMarker", BoolProperty::New(true)); rotatedContourNode->SetBoolProperty("PlanarFigureInitializedWindow", true, m_LastEventSender); rotatedContourNode->SetProperty("includeInBoundingBox", BoolProperty::New(false)); rotatedContourNode->SetProperty("helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes)); rotatedContourNode->SetProperty("planarfigure.drawcontrolpoints", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawname", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawoutline", BoolProperty::New(false)); rotatedContourNode->SetProperty("planarfigure.drawshadow", BoolProperty::New(false)); if (plane) { if (id == size) { m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } else { mitk::NodePredicateProperty::Pointer isMarker = mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer markers = m_ToolManager->GetDataStorage()->GetDerivations(workingNode, isMarker); for (mitk::DataStorage::SetOfObjects::const_iterator iter = markers->begin(); iter != markers->end(); ++iter) { std::string nodeName = (*iter)->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int markerId = atof(nodeName.substr(t + 1).c_str()) - 1; if (id == markerId) { return id; } } m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } } return id; } void mitk::SegTool2D::InteractiveSegmentationBugMessage(const std::string &message) { MITK_ERROR << "********************************************************************************" << std::endl << " " << message << std::endl << "********************************************************************************" << std::endl << " " << std::endl << " If your image is rotated or the 2D views don't really contain the patient image, try to press the " "button next to the image selection. " << std::endl << " " << std::endl << " Please file a BUG REPORT: " << std::endl << " https://phabricator.mitk.org/" << std::endl << " Contain the following information:" << std::endl << " - What image were you working on?" << std::endl << " - Which region of the image?" << std::endl << " - Which tool did you use?" << std::endl << " - What did you do?" << std::endl << " - What happened (not)? What did you expect?" << std::endl; } template void InternalWritePreviewOnWorkingImage(itk::Image *targetSlice, const mitk::Image *sourceSlice, mitk::Image *originalImage, int overwritevalue) { typedef itk::Image SliceType; typename SliceType::Pointer sourceSliceITK; CastToItkImage(sourceSlice, sourceSliceITK); // now the original slice and the ipSegmentation-painted slice are in the same format, and we can just copy all pixels // that are non-zero typedef itk::ImageRegionIterator OutputIteratorType; typedef itk::ImageRegionConstIterator InputIteratorType; InputIteratorType inputIterator(sourceSliceITK, sourceSliceITK->GetLargestPossibleRegion()); OutputIteratorType outputIterator(targetSlice, targetSlice->GetLargestPossibleRegion()); outputIterator.GoToBegin(); inputIterator.GoToBegin(); mitk::LabelSetImage *workingImage = dynamic_cast(originalImage); assert(workingImage); int activePixelValue = workingImage->GetActiveLabel()->GetValue(); if (activePixelValue == 0) // if exterior is the active label { while (!outputIterator.IsAtEnd()) { if (inputIterator.Get() != 0) { outputIterator.Set(overwritevalue); } ++outputIterator; ++inputIterator; } } else if (overwritevalue != 0) // if we are not erasing { while (!outputIterator.IsAtEnd()) { int targetValue = static_cast(outputIterator.Get()); if (inputIterator.Get() != 0) { if (!workingImage->GetLabel(targetValue)->GetLocked()) { outputIterator.Set(overwritevalue); } } if (targetValue == overwritevalue) { outputIterator.Set(inputIterator.Get()); } ++outputIterator; ++inputIterator; } } else // if we are erasing { while (!outputIterator.IsAtEnd()) { const int targetValue = outputIterator.Get(); if (inputIterator.Get() != 0) { if (targetValue == activePixelValue) outputIterator.Set(overwritevalue); } ++outputIterator; ++inputIterator; } } } void mitk::SegTool2D::WritePreviewOnWorkingImage( - Image *targetSlice, Image *sourceSlice, mitk::Image *workingImage, int paintingPixelValue, int timestep) + Image *targetSlice, Image *sourceSlice, mitk::Image *workingImage, int paintingPixelValue, int) { if ((!targetSlice) || (!sourceSlice)) return; AccessFixedDimensionByItk_3( targetSlice, InternalWritePreviewOnWorkingImage, 2, sourceSlice, workingImage, paintingPixelValue); } diff --git a/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp b/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp index 567a6909a4..77fea06761 100644 --- a/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp +++ b/Modules/Segmentation/Interactions/mitkSetRegionTool.cpp @@ -1,189 +1,181 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "mitkSetRegionTool.h" #include "mitkToolManager.h" #include "mitkBaseRenderer.h" #include "mitkImageDataItem.h" #include "mitkLabelSetImage.h" #include "mitkLegacyAdaptors.h" #include #include #include #include #include mitk::SetRegionTool::SetRegionTool(int paintingPixelValue) : FeedbackContourTool("PressMoveRelease"), m_PaintingPixelValue(paintingPixelValue) { } mitk::SetRegionTool::~SetRegionTool() { } void mitk::SetRegionTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("PrimaryButtonPressed", OnMousePressed); CONNECT_FUNCTION("Release", OnMouseReleased); CONNECT_FUNCTION("Move", OnMouseMoved); } void mitk::SetRegionTool::Activated() { Superclass::Activated(); } void mitk::SetRegionTool::Deactivated() { Superclass::Deactivated(); } void mitk::SetRegionTool::OnMousePressed(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); // 1. Get the working image Image::Pointer workingSlice = FeedbackContourTool::GetAffectedWorkingSlice(positionEvent); if (workingSlice.IsNull()) return; // can't do anything without the segmentation // if click was outside the image, don't continue const BaseGeometry *sliceGeometry = workingSlice->GetGeometry(); itk::Index<3> projectedPointIn2D; sliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), projectedPointIn2D); if (!sliceGeometry->IsIndexInside(projectedPointIn2D)) { MITK_ERROR << "point apparently not inside segmentation slice" << std::endl; return; // can't use that as a seed point } typedef itk::Image InputImageType; typedef InputImageType::IndexType IndexType; typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); // convert world coordinates to image indices IndexType seedIndex; sliceGeometry->WorldToIndex(positionEvent->GetPositionInWorld(), seedIndex); // perform region growing in desired segmented region InputImageType::Pointer itkImage = InputImageType::New(); CastToItkImage(workingSlice, itkImage); regionGrower->SetInput(itkImage); regionGrower->AddSeed(seedIndex); InputImageType::PixelType bound = itkImage->GetPixel(seedIndex); regionGrower->SetLower(bound); regionGrower->SetUpper(bound); regionGrower->SetReplaceValue(1); itk::BinaryFillholeImageFilter::Pointer fillHolesFilter = itk::BinaryFillholeImageFilter::New(); fillHolesFilter->SetInput(regionGrower->GetOutput()); fillHolesFilter->SetForegroundValue(1); // Store result and preview mitk::Image::Pointer resultImage = mitk::GrabItkImageMemory(fillHolesFilter->GetOutput()); resultImage->SetGeometry(workingSlice->GetGeometry()); // Get the current working color DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) return; - Image *image = dynamic_cast(workingNode->GetData()); - LabelSetImage *labelImage = dynamic_cast(image); - int activeColor = 1; - if (labelImage != 0) - { - activeColor = labelImage->GetActiveLabel()->GetValue(); - } - mitk::ImageToContourModelFilter::Pointer contourextractor = mitk::ImageToContourModelFilter::New(); contourextractor->SetInput(resultImage); contourextractor->Update(); mitk::ContourModel::Pointer awesomeContour = contourextractor->GetOutput(); FeedbackContourTool::SetFeedbackContour(awesomeContour); FeedbackContourTool::SetFeedbackContourVisible(true); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); } void mitk::SetRegionTool::OnMouseReleased(StateMachineAction *, InteractionEvent *interactionEvent) { mitk::InteractionPositionEvent *positionEvent = dynamic_cast(interactionEvent); if (!positionEvent) return; assert(positionEvent->GetSender()->GetRenderWindow()); // 1. Hide the feedback contour, find out which slice the user clicked, find out which slice of the toolmanager's // working image corresponds to that FeedbackContourTool::SetFeedbackContourVisible(false); mitk::RenderingManager::GetInstance()->RequestUpdate(positionEvent->GetSender()->GetRenderWindow()); int timeStep = positionEvent->GetSender()->GetTimeStep(); DataNode *workingNode(m_ToolManager->GetWorkingData(0)); if (!workingNode) return; Image *image = dynamic_cast(workingNode->GetData()); const PlaneGeometry *planeGeometry((positionEvent->GetSender()->GetCurrentWorldPlaneGeometry())); if (!image || !planeGeometry) return; Image::Pointer slice = FeedbackContourTool::GetAffectedImageSliceAs2DImage(positionEvent, image); if (slice.IsNull()) { MITK_ERROR << "Unable to extract slice." << std::endl; return; } ContourModel *feedbackContour(FeedbackContourTool::GetFeedbackContour()); ContourModel::Pointer projectedContour = FeedbackContourTool::ProjectContourTo2DSlice( slice, feedbackContour, false, false); // false: don't add 0.5 (done by FillContourInSlice) // false: don't constrain the contour to the image's inside if (projectedContour.IsNull()) return; LabelSetImage *labelImage = dynamic_cast(image); int activeColor = 1; if (labelImage != 0) { activeColor = labelImage->GetActiveLabel()->GetValue(); } mitk::ContourModelUtils::FillContourInSlice( projectedContour, timeStep, slice, image, m_PaintingPixelValue * activeColor); this->WriteBackSegmentationResult(positionEvent, slice); } void mitk::SetRegionTool::OnMouseMoved(mitk::StateMachineAction *, mitk::InteractionEvent *) { } diff --git a/Modules/Segmentation/Interactions/mitkTool.cpp b/Modules/Segmentation/Interactions/mitkTool.cpp index f756e36d05..c0b040df0c 100644 --- a/Modules/Segmentation/Interactions/mitkTool.cpp +++ b/Modules/Segmentation/Interactions/mitkTool.cpp @@ -1,323 +1,323 @@ /*=================================================================== 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 "mitkTool.h" #include #include "mitkDisplayInteractor.h" #include "mitkImageReadAccessor.h" #include "mitkImageWriteAccessor.h" #include "mitkLevelWindowProperty.h" #include "mitkLookupTableProperty.h" #include "mitkProperties.h" #include "mitkVtkResliceInterpolationProperty.h" #include // us #include #include // itk #include mitk::Tool::Tool(const char *type) - : m_PredicateImages(NodePredicateDataType::New("Image")) // for reference images - , + : m_EventConfig("DisplayConfigMITK.xml"), + m_ToolManager(nullptr), + m_PredicateImages(NodePredicateDataType::New("Image")), // for reference images m_PredicateDim3(NodePredicateDimension::New(3, 1)), m_PredicateDim4(NodePredicateDimension::New(4, 1)), m_PredicateDimension(mitk::NodePredicateOr::New(m_PredicateDim3, m_PredicateDim4)), m_PredicateImage3D(NodePredicateAnd::New(m_PredicateImages, m_PredicateDimension)), m_PredicateBinary(NodePredicateProperty::New("binary", BoolProperty::New(true))), m_PredicateNotBinary(NodePredicateNot::New(m_PredicateBinary)), m_PredicateSegmentation(NodePredicateProperty::New("segmentation", BoolProperty::New(true))), m_PredicateNotSegmentation(NodePredicateNot::New(m_PredicateSegmentation)), m_PredicateHelper(NodePredicateProperty::New("helper object", BoolProperty::New(true))), m_PredicateNotHelper(NodePredicateNot::New(m_PredicateHelper)), m_PredicateImageColorful(NodePredicateAnd::New(m_PredicateNotBinary, m_PredicateNotSegmentation)), m_PredicateImageColorfulNotHelper(NodePredicateAnd::New(m_PredicateImageColorful, m_PredicateNotHelper)), m_PredicateReference(NodePredicateAnd::New(m_PredicateImage3D, m_PredicateImageColorfulNotHelper)), m_IsSegmentationPredicate( NodePredicateAnd::New(NodePredicateOr::New(m_PredicateBinary, m_PredicateSegmentation), m_PredicateNotHelper)), m_InteractorType(type), - m_DisplayInteractorConfigs(), - m_EventConfig("DisplayConfigMITK.xml") + m_DisplayInteractorConfigs() { } mitk::Tool::~Tool() { } bool mitk::Tool::CanHandle(BaseData *) const { return true; } void mitk::Tool::InitializeStateMachine() { if (m_InteractorType.empty()) return; m_InteractorType += ".xml"; try { LoadStateMachine(m_InteractorType, us::GetModuleContext()->GetModule()); SetEventConfig("SegmentationToolsConfig.xml", us::GetModuleContext()->GetModule()); } catch (const std::exception &e) { MITK_ERROR << "Could not load statemachine pattern " << m_InteractorType << " with exception: " << e.what(); } } void mitk::Tool::Notify(InteractionEvent *interactionEvent, bool isHandled) { // to use the state machine pattern, // the event is passed to the state machine interface to be handled if (!isHandled) { this->HandleEvent(interactionEvent, nullptr); } } void mitk::Tool::ConnectActionsAndFunctions() { } bool mitk::Tool::FilterEvents(InteractionEvent *, DataNode *) { return true; } const char *mitk::Tool::GetGroup() const { return "default"; } void mitk::Tool::SetToolManager(ToolManager *manager) { m_ToolManager = manager; } void mitk::Tool::Activated() { // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled m_DisplayInteractorConfigs.clear(); std::vector> listEventObserver = us::GetModuleContext()->GetServiceReferences(); for (std::vector>::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) { DisplayInteractor *displayInteractor = dynamic_cast(us::GetModuleContext()->GetService(*it)); if (displayInteractor != nullptr) { // remember the original configuration m_DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); // here the alternative configuration is loaded displayInteractor->SetEventConfig(m_EventConfig.c_str()); } } } void mitk::Tool::Deactivated() { // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework for (std::map::iterator it = m_DisplayInteractorConfigs.begin(); it != m_DisplayInteractorConfigs.end(); ++it) { if (it->first) { DisplayInteractor *displayInteractor = static_cast(us::GetModuleContext()->GetService(it->first)); if (displayInteractor != nullptr) { // here the regular configuration is loaded again displayInteractor->SetEventConfig(it->second); } } } m_DisplayInteractorConfigs.clear(); } itk::Object::Pointer mitk::Tool::GetGUI(const std::string &toolkitPrefix, const std::string &toolkitPostfix) { itk::Object::Pointer object; std::string classname = this->GetNameOfClass(); std::string guiClassname = toolkitPrefix + classname + toolkitPostfix; std::list allGUIs = itk::ObjectFactoryBase::CreateAllInstance(guiClassname.c_str()); for (std::list::iterator iter = allGUIs.begin(); iter != allGUIs.end(); ++iter) { if (object.IsNull()) { object = dynamic_cast(iter->GetPointer()); } else { MITK_ERROR << "There is more than one GUI for " << classname << " (several factories claim ability to produce a " << guiClassname << " ) " << std::endl; return nullptr; // people should see and fix this error } } return object; } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetReferenceDataPreference() const { return m_PredicateReference.GetPointer(); } mitk::NodePredicateBase::ConstPointer mitk::Tool::GetWorkingDataPreference() const { return m_IsSegmentationPredicate.GetPointer(); } mitk::DataNode::Pointer mitk::Tool::CreateEmptySegmentationNode(Image *original, const std::string &organName, const mitk::Color &color) { // we NEED a reference image for size etc. if (!original) return nullptr; // actually create a new empty segmentation PixelType pixelType(mitk::MakeScalarPixelType()); LabelSetImage::Pointer segmentation = LabelSetImage::New(); if (original->GetDimension() == 2) { const unsigned int dimensions[] = {original->GetDimension(0), original->GetDimension(1), 1}; segmentation->Initialize(pixelType, 3, dimensions); segmentation->AddLayer(); } else { segmentation->Initialize(original); } mitk::Label::Pointer label = mitk::Label::New(); label->SetName(organName); label->SetColor(color); label->SetValue(1); segmentation->GetActiveLabelSet()->AddLabel(label); segmentation->GetActiveLabelSet()->SetActiveLabel(1); unsigned int byteSize = sizeof(mitk::Label::PixelType); if (segmentation->GetDimension() < 4) { for (unsigned int dim = 0; dim < segmentation->GetDimension(); ++dim) { byteSize *= segmentation->GetDimension(dim); } mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(0)); memset(writeAccess.GetData(), 0, byteSize); } else { // if we have a time-resolved image we need to set memory to 0 for each time step for (unsigned int dim = 0; dim < 3; ++dim) { byteSize *= segmentation->GetDimension(dim); } for (unsigned int volumeNumber = 0; volumeNumber < segmentation->GetDimension(3); volumeNumber++) { mitk::ImageWriteAccessor writeAccess(segmentation.GetPointer(), segmentation->GetVolumeData(volumeNumber)); memset(writeAccess.GetData(), 0, byteSize); } } if (original->GetTimeGeometry()) { TimeGeometry::Pointer originalGeometry = original->GetTimeGeometry()->Clone(); segmentation->SetTimeGeometry(originalGeometry); } else { Tool::ErrorMessage("Original image does not have a 'Time sliced geometry'! Cannot create a segmentation."); return nullptr; } // Add some DICOM Tags as properties to segmentation image PropertyList::Pointer dicomSegPropertyList = mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentationProperties(original->GetPropertyList()); segmentation->GetPropertyList()->ConcatenatePropertyList(dicomSegPropertyList); mitk::DICOMSegmentationPropertyHandler::GetDICOMSegmentProperties(segmentation->GetActiveLabel(segmentation->GetActiveLayer())); return CreateSegmentationNode(segmentation, organName, color); } mitk::DataNode::Pointer mitk::Tool::CreateSegmentationNode(Image *image, const std::string &organName, const mitk::Color &color) { if (!image) return nullptr; // decorate the datatreenode with some properties DataNode::Pointer segmentationNode = DataNode::New(); segmentationNode->SetData(image); // name segmentationNode->SetProperty("name", StringProperty::New(organName)); // visualization properties segmentationNode->SetProperty("binary", BoolProperty::New(true)); segmentationNode->SetProperty("color", ColorProperty::New(color)); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); lut->SetType(mitk::LookupTable::MULTILABEL); mitk::LookupTableProperty::Pointer lutProp = mitk::LookupTableProperty::New(); lutProp->SetLookupTable(lut); segmentationNode->SetProperty("LookupTable", lutProp); segmentationNode->SetProperty("texture interpolation", BoolProperty::New(false)); segmentationNode->SetProperty("layer", IntProperty::New(10)); segmentationNode->SetProperty("levelwindow", LevelWindowProperty::New(LevelWindow(0.5, 1))); segmentationNode->SetProperty("opacity", FloatProperty::New(0.3)); segmentationNode->SetProperty("segmentation", BoolProperty::New(true)); segmentationNode->SetProperty("reslice interpolation", VtkResliceInterpolationProperty::New()); // otherwise -> segmentation appears in 2 // slices sometimes (only visual effect, not // different data) // For MITK-3M3 release, the volume of all segmentations should be shown segmentationNode->SetProperty("showVolume", BoolProperty::New(true)); return segmentationNode; } us::ModuleResource mitk::Tool::GetIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } us::ModuleResource mitk::Tool::GetCursorIconResource() const { // Each specific tool should load its own resource. This one will be invalid return us::ModuleResource(); } diff --git a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp index 04cc20d065..8a8a185471 100644 --- a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp +++ b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterObliquePlaneTest.cpp @@ -1,258 +1,258 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include int ObliquePlaneTestVolumeSize = 128; static void OverwriteObliquePlaneTest(mitk::Image *workingImage, mitk::Image *refImg) { /*==============TEST WITHOUT MITK CONVERTION=============================*/ /* ============= setup plane ============*/ int sliceindex = (int)(ObliquePlaneTestVolumeSize / 2); // rand() % 32; bool isFrontside = true; bool isRotated = false; mitk::PlaneGeometry::Pointer obliquePlane = mitk::PlaneGeometry::New(); obliquePlane->InitializeStandardPlane( workingImage->GetGeometry(), mitk::PlaneGeometry::Axial, sliceindex, isFrontside, isRotated); mitk::Point3D origin = obliquePlane->GetOrigin(); mitk::Vector3D normal; normal = obliquePlane->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 obliquePlane->SetOrigin(origin); mitk::Vector3D rotationVector = obliquePlane->GetAxisVector(0); rotationVector.Normalize(); float degree = 45.0; auto op = new mitk::RotationOperation(mitk::OpROTATE, obliquePlane->GetCenter(), rotationVector, degree); obliquePlane->ExecuteOperation(op); delete op; /* ============= extract slice ============*/ mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(); slicer->SetInput(workingImage); slicer->SetWorldGeometry(obliquePlane); slicer->SetVtkOutputRequest(true); slicer->Modified(); slicer->Update(); vtkSmartPointer slice = vtkSmartPointer::New(); slice = slicer->GetVtkOutput(); /* ============= overwrite slice ============*/ vtkSmartPointer resliceIdx = vtkSmartPointer::New(); mitk::ExtractSliceFilter::Pointer overwriter = mitk::ExtractSliceFilter::New(resliceIdx); resliceIdx->SetOverwriteMode(true); resliceIdx->SetInputSlice(slice); resliceIdx->Modified(); overwriter->SetInput(workingImage); overwriter->SetWorldGeometry(obliquePlane); overwriter->SetVtkOutputRequest(true); overwriter->Modified(); overwriter->Update(); typedef mitk::ImagePixelReadAccessor ReadAccessorType; ReadAccessorType refImgReadAccessor(refImg); ReadAccessorType workingImgReadAccessor(workingImage); /* ============= check ref == working ============*/ bool areSame = true; itk::Index<3> id; id[0] = id[1] = id[2] = 0; for (int x = 0; x < ObliquePlaneTestVolumeSize; ++x) { id[0] = x; for (int y = 0; y < ObliquePlaneTestVolumeSize; ++y) { id[1] = y; for (int z = 0; z < ObliquePlaneTestVolumeSize; ++z) { id[2] = z; areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id); if (!areSame) goto stop; } } } stop: MITK_TEST_CONDITION(areSame, "comparing images (no mitk convertion) [oblique]"); /*==============TEST WITH MITK CONVERTION=============================*/ /* ============= extract slice ============*/ mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New(); slicer2->SetInput(workingImage); slicer2->SetWorldGeometry(obliquePlane); slicer2->Modified(); slicer2->Update(); mitk::Image::Pointer sliceInMitk = slicer2->GetOutput(); vtkSmartPointer slice2 = vtkSmartPointer::New(); slice2 = sliceInMitk->GetVtkImageData(); /* ============= overwrite slice ============*/ vtkSmartPointer resliceIdx2 = vtkSmartPointer::New(); mitk::ExtractSliceFilter::Pointer overwriter2 = mitk::ExtractSliceFilter::New(resliceIdx2); resliceIdx2->SetOverwriteMode(true); resliceIdx2->SetInputSlice(slice2); resliceIdx2->Modified(); overwriter2->SetInput(workingImage); overwriter2->SetWorldGeometry(obliquePlane); overwriter2->SetVtkOutputRequest(true); overwriter2->Modified(); overwriter2->Update(); /* ============= check ref == working ============*/ areSame = true; id[0] = id[1] = id[2] = 0; for (int x = 0; x < ObliquePlaneTestVolumeSize; ++x) { id[0] = x; for (int y = 0; y < ObliquePlaneTestVolumeSize; ++y) { id[1] = y; for (int z = 0; z < ObliquePlaneTestVolumeSize; ++z) { id[2] = z; areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id); if (!areSame) goto stop2; } } } stop2: MITK_TEST_CONDITION(areSame, "comparing images (with mitk convertion) [oblique]"); /*==============TEST EDIT WITHOUT MITK CONVERTION=============================*/ /* ============= edit slice ============*/ int idX = std::abs(ObliquePlaneTestVolumeSize - 59); int idY = std::abs(ObliquePlaneTestVolumeSize - 23); int idZ = 0; int component = 0; double val = 33.0; slice->SetScalarComponentFromDouble(idX, idY, idZ, component, val); mitk::Vector3D indx; indx[0] = idX; indx[1] = idY; indx[2] = idZ; sliceInMitk->GetGeometry()->IndexToWorld(indx, indx); /* ============= overwrite slice ============*/ vtkSmartPointer resliceIdx3 = vtkSmartPointer::New(); resliceIdx3->SetOverwriteMode(true); resliceIdx3->SetInputSlice(slice); mitk::ExtractSliceFilter::Pointer overwriter3 = mitk::ExtractSliceFilter::New(resliceIdx3); overwriter3->SetInput(workingImage); overwriter3->SetWorldGeometry(obliquePlane); overwriter3->SetVtkOutputRequest(true); overwriter3->Modified(); overwriter3->Update(); /* ============= check ============*/ areSame = true; - int x, y, z; + int x = 0; + int y = 0; + int z = 0; for (x = 0; x < ObliquePlaneTestVolumeSize; ++x) { id[0] = x; for (y = 0; y < ObliquePlaneTestVolumeSize; ++y) { id[1] = y; for (z = 0; z < ObliquePlaneTestVolumeSize; ++z) { id[2] = z; areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id); if (!areSame) goto stop3; } } } stop3: - // MITK_INFO << "index: [" << x << ", " << y << ", " << z << "]"; - // MITK_INFO << indx; MITK_TEST_CONDITION(x == idX && y == z, "overwrited the right index [oblique]"); } /*================ #BEGIN test main ================*/ int mitkOverwriteSliceFilterObliquePlaneTest(int, char *[]) { MITK_TEST_BEGIN("mitkOverwriteSliceFilterObliquePlaneTest") typedef itk::Image ImageType; typedef itk::ImageRegionConstIterator ImageIterator; ImageType::Pointer image = ImageType::New(); ImageType::IndexType start; start[0] = start[1] = start[2] = 0; ImageType::SizeType size; size[0] = size[1] = size[2] = ObliquePlaneTestVolumeSize; ImageType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); image->SetRegions(imgRegion); image->SetSpacing(1.0); image->Allocate(); ImageIterator imageIterator(image, image->GetLargestPossibleRegion()); imageIterator.GoToBegin(); unsigned short pixelValue = 0; // fill the image with distinct values while (!imageIterator.IsAtEnd()) { image->SetPixel(imageIterator.GetIndex(), pixelValue); ++imageIterator; ++pixelValue; } /* end setup itk image */ mitk::Image::Pointer refImage; CastToMitkImage(image, refImage); mitk::Image::Pointer workingImg; CastToMitkImage(image, workingImg); OverwriteObliquePlaneTest(workingImg, refImage); MITK_TEST_END() } diff --git a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp index 3dbef0201e..fe1c4c21b3 100644 --- a/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp +++ b/Modules/Segmentation/Testing/mitkOverwriteSliceFilterTest.cpp @@ -1,183 +1,185 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include int VolumeSize = 128; /*================ #BEGIN test main ================*/ int mitkOverwriteSliceFilterTest(int, char *[]) { MITK_TEST_BEGIN("mitkOverwriteSliceFilterTest") typedef itk::Image ImageType; typedef itk::ImageRegionConstIterator ImageIterator; ImageType::Pointer image = ImageType::New(); ImageType::IndexType start; start[0] = start[1] = start[2] = 0; ImageType::SizeType size; size[0] = size[1] = size[2] = VolumeSize; ImageType::RegionType imgRegion; imgRegion.SetSize(size); imgRegion.SetIndex(start); image->SetRegions(imgRegion); image->SetSpacing(1.0); image->Allocate(); ImageIterator imageIterator(image, image->GetLargestPossibleRegion()); imageIterator.GoToBegin(); unsigned short pixelValue = 0; // fill the image with distinct values while (!imageIterator.IsAtEnd()) { image->SetPixel(imageIterator.GetIndex(), pixelValue); ++imageIterator; ++pixelValue; } /* end setup itk image */ mitk::Image::Pointer referenceImage; CastToMitkImage(image, referenceImage); mitk::Image::Pointer workingImage; CastToMitkImage(image, workingImage); typedef mitk::ImagePixelReadAccessor ReadAccessorType; ReadAccessorType refImgReadAccessor(referenceImage); ReadAccessorType workingImgReadAccessor(workingImage); /* ============= setup plane ============*/ int sliceindex = 55; // rand() % 32; bool isFrontside = true; bool isRotated = false; mitk::PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane( workingImage->GetGeometry(), mitk::PlaneGeometry::Axial, sliceindex, isFrontside, isRotated); mitk::Point3D origin = plane->GetOrigin(); mitk::Vector3D normal; normal = plane->GetNormal(); normal.Normalize(); origin += normal * 0.5; // pixelspacing is 1, so half the spacing is 0.5 plane->SetOrigin(origin); /* ============= extract slice ============*/ vtkSmartPointer resliceIdx = vtkSmartPointer::New(); mitk::ExtractSliceFilter::Pointer slicer = mitk::ExtractSliceFilter::New(resliceIdx); slicer->SetInput(workingImage); slicer->SetWorldGeometry(plane); slicer->SetVtkOutputRequest(true); slicer->Modified(); slicer->Update(); vtkSmartPointer slice = vtkSmartPointer::New(); slice = slicer->GetVtkOutput(); /* ============= overwrite slice ============*/ resliceIdx->SetOverwriteMode(true); resliceIdx->Modified(); slicer->Modified(); slicer->Update(); // implicit overwrite /* ============= check ref == working ============*/ bool areSame = true; itk::Index<3> id; id[0] = id[1] = id[2] = 0; for (int x = 0; x < VolumeSize; ++x) { id[0] = x; for (int y = 0; y < VolumeSize; ++y) { id[1] = y; for (int z = 0; z < VolumeSize; ++z) { id[2] = z; areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id); if (!areSame) goto stop; } } } stop: MITK_TEST_CONDITION(areSame, "test overwrite unmodified slice"); /* ============= edit slice ============*/ int idX = std::abs(VolumeSize - 59); int idY = std::abs(VolumeSize - 23); int idZ = 0; int component = 0; double val = 33.0; slice->SetScalarComponentFromDouble(idX, idY, idZ, component, val); /* ============= overwrite slice ============*/ vtkSmartPointer resliceIdx2 = vtkSmartPointer::New(); resliceIdx2->SetOverwriteMode(true); resliceIdx2->SetInputSlice(slice); mitk::ExtractSliceFilter::Pointer slicer2 = mitk::ExtractSliceFilter::New(resliceIdx2); slicer2->SetInput(workingImage); slicer2->SetWorldGeometry(plane); slicer2->SetVtkOutputRequest(true); slicer2->Modified(); slicer2->Update(); /* ============= check ============*/ areSame = true; - int xx, yy, zz; + int xx = 0; + int yy = 0; + int zz = 0; for (xx = 0; xx < VolumeSize; ++xx) { id[0] = xx; for (yy = 0; yy < VolumeSize; ++yy) { id[1] = yy; for (zz = 0; zz < VolumeSize; ++zz) { id[2] = zz; areSame = refImgReadAccessor.GetPixelByIndex(id) == workingImgReadAccessor.GetPixelByIndex(id); if (!areSame) goto stop2; } } } stop2: // MITK_INFO << "index: [" << x << ", " << y << ", " << z << "]"; MITK_TEST_CONDITION(xx == idX && yy == idY && zz == sliceindex, "test overwrite modified slice"); MITK_TEST_END() } diff --git a/Modules/Segmentation/Testing/mitkSegmentationInterpolationTest.cpp b/Modules/Segmentation/Testing/mitkSegmentationInterpolationTest.cpp index 1335704aa0..f5618bd889 100644 --- a/Modules/Segmentation/Testing/mitkSegmentationInterpolationTest.cpp +++ b/Modules/Segmentation/Testing/mitkSegmentationInterpolationTest.cpp @@ -1,208 +1,208 @@ /*=================================================================== 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. ===================================================================*/ // Testing #include #include // other #include #include #include #include #include #include #include #include #include class mitkSegmentationInterpolationTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSegmentationInterpolationTestSuite); MITK_TEST(Equal_Axial_TestInterpolationAndReferenceInterpolation_ReturnsTrue); MITK_TEST(Equal_Frontal_TestInterpolationAndReferenceInterpolation_ReturnsTrue); MITK_TEST(Equal_Sagittal_TestInterpolationAndReferenceInterpolation_ReturnsTrue); CPPUNIT_TEST_SUITE_END(); private: // The tests all do the same, only in different directions void testRoutine(mitk::SliceNavigationController::ViewDirection viewDirection) { int dim; switch (viewDirection) { case (mitk::SliceNavigationController::Axial): dim = 2; break; case (mitk::SliceNavigationController::Frontal): dim = 1; break; case (mitk::SliceNavigationController::Sagittal): dim = 0; break; - case (mitk::SliceNavigationController::Original): + default: // mitk::SliceNavigationController::Original dim = -1; - break; // This is just to get rid of a warning + break; } /* Fill segmentation * * 1st slice: 3x3 square segmentation * 2nd slice: empty * 3rd slice: 1x1 square segmentation in corner * -> 2nd slice should become 2x2 square in corner * * put accessor in scope */ itk::Index<3> currentPoint; { mitk::ImagePixelWriteAccessor writeAccessor(m_SegmentationImage); // Fill 3x3 slice currentPoint[dim] = m_CenterPoint[dim] - 1; for (int i = -1; i <= 1; ++i) { for (int j = -1; j <= 1; ++j) { currentPoint[(dim + 1) % 3] = m_CenterPoint[(dim + 1) % 3] + i; currentPoint[(dim + 2) % 3] = m_CenterPoint[(dim + 2) % 3] + j; writeAccessor.SetPixelByIndexSafe(currentPoint, 1); } } // Now i=j=1, set point two slices up currentPoint[dim] = m_CenterPoint[dim] + 1; writeAccessor.SetPixelByIndexSafe(currentPoint, 1); } // mitk::IOUtil::Save(m_SegmentationImage, "SOME PATH"); m_InterpolationController->SetSegmentationVolume(m_SegmentationImage); m_InterpolationController->SetReferenceVolume(m_ReferenceImage); // This could be easier... mitk::SliceNavigationController::Pointer navigationController = mitk::SliceNavigationController::New(); navigationController->SetInputWorldTimeGeometry(m_SegmentationImage->GetTimeGeometry()); navigationController->Update(viewDirection); mitk::Point3D pointMM; m_SegmentationImage->GetTimeGeometry()->GetGeometryForTimeStep(0)->IndexToWorld(m_CenterPoint, pointMM); navigationController->SelectSliceByPoint(pointMM); auto plane = navigationController->GetCurrentPlaneGeometry(); mitk::Image::Pointer interpolationResult = m_InterpolationController->Interpolate(dim, m_CenterPoint[dim], plane, 0); // mitk::IOUtil::Save(interpolationResult, "SOME PATH"); // Write result into segmentation image vtkSmartPointer reslicer = vtkSmartPointer::New(); reslicer->SetInputSlice( interpolationResult->GetSliceData()->GetVtkImageAccessor(interpolationResult)->GetVtkImageData()); reslicer->SetOverwriteMode(true); reslicer->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslicer); extractor->SetInput(m_SegmentationImage); extractor->SetTimeStep(0); extractor->SetWorldGeometry(plane); extractor->SetVtkOutputRequest(true); extractor->SetResliceTransformByGeometry(m_SegmentationImage->GetTimeGeometry()->GetGeometryForTimeStep(0)); extractor->Modified(); extractor->Update(); // mitk::IOUtil::Save(m_SegmentationImage, "SOME PATH"); // Check a 4x4 square, the center of which needs to be filled mitk::ImagePixelReadAccessor readAccess(m_SegmentationImage); currentPoint = m_CenterPoint; for (int i = -1; i <= 2; ++i) { for (int j = -1; j <= 2; ++j) { currentPoint[(dim + 1) % 3] = m_CenterPoint[(dim + 1) % 3] + i; currentPoint[(dim + 2) % 3] = m_CenterPoint[(dim + 2) % 3] + j; if (i == -1 || i == 2 || j == -1 || j == 2) { CPPUNIT_ASSERT_MESSAGE("Have false positive segmentation.", readAccess.GetPixelByIndexSafe(currentPoint) == 0); } else { CPPUNIT_ASSERT_MESSAGE("Have false negative segmentation.", readAccess.GetPixelByIndexSafe(currentPoint) == 1); } } } } mitk::Image::Pointer m_ReferenceImage; mitk::Image::Pointer m_SegmentationImage; itk::Index<3> m_CenterPoint; mitk::SegmentationInterpolationController::Pointer m_InterpolationController; public: void setUp() override { m_ReferenceImage = mitk::IOUtil::LoadImage(GetTestDataFilePath("Pic3D.nrrd")); CPPUNIT_ASSERT_MESSAGE("Failed to load image for test: [Pic3D.nrrd]", m_ReferenceImage.IsNotNull()); m_InterpolationController = mitk::SegmentationInterpolationController::GetInstance(); // Create empty segmentation // Surely there must be a better way to get an image with all zeros? m_SegmentationImage = mitk::Image::New(); const mitk::PixelType pixelType(mitk::MakeScalarPixelType()); m_SegmentationImage->Initialize(pixelType, m_ReferenceImage->GetDimension(), m_ReferenceImage->GetDimensions()); m_SegmentationImage->SetClonedTimeGeometry(m_ReferenceImage->GetTimeGeometry()); unsigned int size = sizeof(mitk::Tool::DefaultSegmentationDataType); for (unsigned int dim = 0; dim < m_SegmentationImage->GetDimension(); ++dim) { size *= m_SegmentationImage->GetDimension(dim); } mitk::ImageWriteAccessor imageAccessor(m_SegmentationImage); memset(imageAccessor.GetData(), 0, size); // Work in the center of the image (Pic3D) m_CenterPoint = {{127, 127, 25}}; } void tearDown() override { m_ReferenceImage = nullptr; m_SegmentationImage = nullptr; m_CenterPoint = {{0, 0, 0}}; } void Equal_Axial_TestInterpolationAndReferenceInterpolation_ReturnsTrue() { mitk::SliceNavigationController::ViewDirection viewDirection = mitk::SliceNavigationController::Axial; testRoutine(viewDirection); } void Equal_Frontal_TestInterpolationAndReferenceInterpolation_ReturnsTrue() // Coronal { mitk::SliceNavigationController::ViewDirection viewDirection = mitk::SliceNavigationController::Frontal; testRoutine(viewDirection); } void Equal_Sagittal_TestInterpolationAndReferenceInterpolation_ReturnsTrue() { mitk::SliceNavigationController::ViewDirection viewDirection = mitk::SliceNavigationController::Sagittal; testRoutine(viewDirection); } }; MITK_TEST_SUITE_REGISTRATION(mitkSegmentationInterpolation) diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp index 754658f509..75b5c52e80 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp @@ -1,969 +1,968 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkAdaptiveRegionGrowingToolGUI.h" #include "QmitkStdMultiWidget.h" #include #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 #include #include #include #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(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::Pointer pointAddedCommand = itk::SimpleMemberCommand::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(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 static void AccessPixel(mitk::PixelType /*ptype*/, const mitk::Image::Pointer im, mitk::Point3D p, int &val) { mitk::ImagePixelReadAccessor 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(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(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::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::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::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(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(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 void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(itk::Image *itkImage, mitk::BaseGeometry *imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); - typedef itk::MinimumMaximumImageCalculator MinMaxValueFilterType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::MaskImageFilter 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(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::max()); } else { thresholdFilter->SetLowerThreshold(itk::NumericTraits::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(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(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(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(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(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 void QmitkAdaptiveRegionGrowingToolGUI::ITKThresholding(itk::Image *itkImage) { mitk::Image::Pointer originalSegmentation = dynamic_cast(this->m_RegionGrow3DTool->GetTargetSegmentationNode()->GetData()); int timeStep = mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))->GetTimeStep(); if (originalSegmentation) { typedef itk::Image InputImageType; typedef itk::Image 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 itOutput(originalSegmentationInITK, originalSegmentationInITK->GetLargestPossibleRegion()); itk::ImageRegionIterator 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() >= static_cast::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) +void QmitkAdaptiveRegionGrowingToolGUI::UpdateVolumeRenderingThreshold(int) { typedef short PixelType; typedef itk::Image InputImageType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::MaskImageFilter MaskImageFilterType; mitk::DataNode::Pointer grownImageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::Image::Pointer grownImage = dynamic_cast(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::min(); if (m_CurrentRGDirectionIsUpwards) { threshold = static_cast(m_UPPERTHRESHOLD - sliderVal + 0.5); thresholdFilter->SetLowerThreshold(threshold); thresholdFilter->SetUpperThreshold(itk::NumericTraits::max()); } else { threshold = sliderVal - m_LOWERTHRESHOLD + 0.5; thresholdFilter->SetLowerThreshold(itk::NumericTraits::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(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/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp index df9c914bf3..4020341172 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp @@ -1,1311 +1,1311 @@ /*=================================================================== 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 "QmitkLabelSetWidget.h" // mitk #include #include #include #include #include #include #include #include #include #include #include // Qmitk #include #include #include // Qt #include #include #include #include #include #include #include #include #include // itk #include // todo: // berry //#include QmitkLabelSetWidget::QmitkLabelSetWidget(QWidget *parent) - : QWidget(parent), m_ToolManager(nullptr), m_DataStorage(nullptr), m_Completer(nullptr) + : QWidget(parent), m_DataStorage(nullptr), m_Completer(nullptr), m_ToolManager(nullptr) { m_Controls.setupUi(this); m_ColorSequenceRainbow.GoToBegin(); m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); assert(m_ToolManager); m_Controls.m_LabelSearchBox->setAlwaysShowClearIcon(true); m_Controls.m_LabelSearchBox->setShowSearchIcon(true); QStringList completionList; completionList << ""; m_Completer = new QCompleter(completionList, this); m_Completer->setCaseSensitivity(Qt::CaseInsensitive); m_Controls.m_LabelSearchBox->setCompleter(m_Completer); connect(m_Controls.m_LabelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel())); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(labelListModified(const QStringList&)), this, SLOT( // OnLabelListModified(const QStringList&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(mergeLabel(int)), this, SLOT( OnMergeLabel(int)) ); QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(GetLabelStringList()); m_Controls.m_LabelSearchBox->setEnabled(false); m_Controls.m_lblCaption->setText(""); InitializeTableWidget(); } QmitkLabelSetWidget::~QmitkLabelSetWidget() { } void QmitkLabelSetWidget::OnTableViewContextMenuRequested(const QPoint& /*pos*/) { int pixelValue = GetPixelValueOfSelectedItem(); QMenu *menu = new QMenu(m_Controls.m_LabelSetTableWidget); if (m_Controls.m_LabelSetTableWidget->selectedItems().size() > 1) { QAction *mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this); mergeAction->setEnabled(true); QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabels(bool))); menu->addAction(mergeAction); QAction *removeLabelsAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove selected labels", this); removeLabelsAction->setEnabled(true); QObject::connect(removeLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabels(bool))); menu->addAction(removeLabelsAction); QAction *eraseLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase selected labels", this); eraseLabelsAction->setEnabled(true); QObject::connect(eraseLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabels(bool))); menu->addAction(eraseLabelsAction); QAction *combineAndCreateSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Combine and create a surface", this); combineAndCreateSurfaceAction->setEnabled(true); QObject::connect( combineAndCreateSurfaceAction, SIGNAL(triggered(bool)), this, SLOT(OnCombineAndCreateSurface(bool))); // menu->addAction(combineAndCreateSurfaceAction); Not implemented QAction *createMasksAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create a mask for each selected label", this); createMasksAction->setEnabled(true); QObject::connect(createMasksAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMasks(bool))); // menu->addAction(createMasksAction); Not implemented QAction *combineAndCreateMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Combine and create a mask", this); combineAndCreateMaskAction->setEnabled(true); QObject::connect(combineAndCreateMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCombineAndCreateMask(bool))); // menu->addAction(combineAndCreateMaskAction); Not implemented } else { QAction *renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Rename...", this); renameAction->setEnabled(true); QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool))); menu->addAction(renameAction); QAction *removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove...", this); removeAction->setEnabled(true); QObject::connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabel(bool))); menu->addAction(removeAction); QAction *eraseAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase...", this); eraseAction->setEnabled(true); QObject::connect(eraseAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabel(bool))); menu->addAction(eraseAction); QAction *mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge...", this); mergeAction->setEnabled(true); QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabel(bool))); menu->addAction(mergeAction); QAction *randomColorAction = new QAction(QIcon(":/Qmitk/RandomColor.png"), "Random color", this); randomColorAction->setEnabled(true); QObject::connect(randomColorAction, SIGNAL(triggered(bool)), this, SLOT(OnRandomColor(bool))); menu->addAction(randomColorAction); QAction *viewOnlyAction = new QAction(QIcon(":/Qmitk/visible.png"), "View only", this); viewOnlyAction->setEnabled(true); QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool))); menu->addAction(viewOnlyAction); QAction *viewAllAction = new QAction(QIcon(":/Qmitk/visible.png"), "View all", this); viewAllAction->setEnabled(true); QObject::connect(viewAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsVisible(bool))); menu->addAction(viewAllAction); QAction *hideAllAction = new QAction(QIcon(":/Qmitk/invisible.png"), "Hide all", this); hideAllAction->setEnabled(true); QObject::connect(hideAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsInvisible(bool))); menu->addAction(hideAllAction); QAction *lockAllAction = new QAction(QIcon(":/Qmitk/lock.png"), "Lock all", this); lockAllAction->setEnabled(true); QObject::connect(lockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnLockAllLabels(bool))); menu->addAction(lockAllAction); QAction *unlockAllAction = new QAction(QIcon(":/Qmitk/unlock.png"), "Unlock all", this); unlockAllAction->setEnabled(true); QObject::connect(unlockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnUnlockAllLabels(bool))); menu->addAction(unlockAllAction); QAction *createSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Create surface", this); createSurfaceAction->setEnabled(true); createSurfaceAction->setMenu(new QMenu()); QAction *tmp1 = createSurfaceAction->menu()->addAction(QString("Detailed")); QAction *tmp2 = createSurfaceAction->menu()->addAction(QString("Smoothed")); QObject::connect(tmp1, SIGNAL(triggered(bool)), this, SLOT(OnCreateDetailedSurface(bool))); QObject::connect(tmp2, SIGNAL(triggered(bool)), this, SLOT(OnCreateSmoothedSurface(bool))); menu->addAction(createSurfaceAction); QAction *createMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create mask", this); createMaskAction->setEnabled(true); QObject::connect(createMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMask(bool))); menu->addAction(createMaskAction); QAction *createCroppedMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create cropped mask", this); createCroppedMaskAction->setEnabled(true); QObject::connect(createCroppedMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateCroppedMask(bool))); // QAction* importAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Import...", this ); // importAction->setEnabled(true); // QObject::connect( importAction, SIGNAL( triggered(bool) ), this, SLOT( OnImportSegmentationSession(bool) ) ); // menu->addAction(importAction); menu->addAction(createCroppedMaskAction); QSlider *opacitySlider = new QSlider; opacitySlider->setMinimum(0); opacitySlider->setMaximum(100); opacitySlider->setOrientation(Qt::Horizontal); QObject::connect(opacitySlider, SIGNAL(valueChanged(int)), this, SLOT(OnOpacityChanged(int))); QLabel *_OpacityLabel = new QLabel("Opacity: "); QVBoxLayout *_OpacityWidgetLayout = new QVBoxLayout; _OpacityWidgetLayout->setContentsMargins(4, 4, 4, 4); _OpacityWidgetLayout->addWidget(_OpacityLabel); _OpacityWidgetLayout->addWidget(opacitySlider); QWidget *_OpacityWidget = new QWidget; _OpacityWidget->setLayout(_OpacityWidgetLayout); QWidgetAction *OpacityAction = new QWidgetAction(this); OpacityAction->setDefaultWidget(_OpacityWidget); // QObject::connect( m_OpacityAction, SIGNAL( changed() ), this, SLOT( OpacityActionChanged() ) ); opacitySlider->setValue(static_cast(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetOpacity() * 100)); menu->addAction(OpacityAction); } menu->popup(QCursor::pos()); } void QmitkLabelSetWidget::OnUnlockAllLabels(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnLockAllLabels(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnSetAllLabelsVisible(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(true); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnSetAllLabelsInvisible(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(false); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnSetOnlyActiveLabelVisible(bool /*value*/) { mitk::LabelSetImage* workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); workingImage->GetActiveLabelSet()->SetAllLabelsVisible(false); workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->SetVisible(true); workingImage->GetActiveLabelSet()->UpdateLookupTable(pixelValue); this->WaitCursorOn(); const mitk::Point3D &pos = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates(); this->WaitCursorOff(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnMergeLabel(bool /*value*/) { QmitkSearchLabelDialog dialog(this); dialog.setWindowTitle("Select a second label.."); dialog.SetLabelSuggestionList(GetLabelStringList()); int dialogReturnValue = dialog.exec(); if (dialogReturnValue == QDialog::Rejected) return; int sourcePixelValue = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); i++) { if (dialog.GetLabelSetWidgetTableCompleteWord() == QString(m_Controls.m_LabelSetTableWidget->item(i, 0)->text())) sourcePixelValue = m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt(); } if (sourcePixelValue == -1) { MITK_INFO << "unknown label"; return; } int pixelValue = GetPixelValueOfSelectedItem(); GetWorkingImage()->MergeLabel(pixelValue, sourcePixelValue, GetWorkingImage()->GetActiveLayer()); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnEraseLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to erase the contents of label \""; question.append(QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question(this, "Erase label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { this->WaitCursorOn(); GetWorkingImage()->EraseLabel(pixelValue); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnRemoveLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to remove label \""; question.append(QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question(this, "Remove label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { this->WaitCursorOn(); GetWorkingImage()->GetActiveLabelSet()->RemoveLabel(pixelValue); GetWorkingImage()->EraseLabel(pixelValue); this->WaitCursorOff(); } ResetAllTableWidgetItems(); } void QmitkLabelSetWidget::OnRenameLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QmitkNewSegmentationDialog dialog(this); dialog.setWindowTitle("Rename Label"); dialog.SetSuggestionList(m_OrganColors); dialog.SetColor(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetColor()); dialog.SetSegmentationName(QString::fromStdString(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetName())); if (dialog.exec() == QDialog::Rejected) { return; } QString segmentationName = dialog.GetSegmentationName(); if (segmentationName.isEmpty()) { segmentationName = "Unnamed"; } GetWorkingImage()->GetActiveLabelSet()->RenameLabel(pixelValue, segmentationName.toStdString(), dialog.GetColor()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnCombineAndCreateMask(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ...to do... // } void QmitkLabelSetWidget::OnCreateMasks(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ..to do.. // } void QmitkLabelSetWidget::OnCombineAndCreateSurface(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ..to do.. // } void QmitkLabelSetWidget::OnEraseLabels(bool /*value*/) { QString question = "Do you really want to erase the selected labels?"; QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Erase selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) return; std::vector VectorOfLablePixelValues; foreach (QTableWidgetSelectionRange a, ranges) for (int i = a.topRow(); i <= a.bottomRow(); i++) VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); this->WaitCursorOn(); GetWorkingImage()->EraseLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnRemoveLabels(bool /*value*/) { QString question = "Do you really want to remove selected labels?"; QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Remove selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) { return; } std::vector VectorOfLablePixelValues; foreach(QTableWidgetSelectionRange a, ranges) { for (int i = a.topRow(); i <= a.bottomRow(); ++i) { VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); } } this->WaitCursorOn(); GetWorkingImage()->RemoveLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); } ResetAllTableWidgetItems(); } void QmitkLabelSetWidget::OnMergeLabels(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to merge selected labels into \""; question.append(QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Merge selected label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) { return; } std::vector vectorOfSourcePixelValues; foreach(QTableWidgetSelectionRange a, ranges) { for (int i = a.topRow(); i <= a.bottomRow(); ++i) { vectorOfSourcePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); } } this->WaitCursorOn(); GetWorkingImage()->MergeLabels(pixelValue, vectorOfSourcePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnLockedButtonClicked() { - int row; + int row = -1; for(int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, LOCKED_COL)) { row = i; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt(); GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetLocked(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetLocked()); } } void QmitkLabelSetWidget::OnVisibleButtonClicked() { - int row; + int row = -1; for(int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, VISIBLE_COL)) { row = i; break; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { QTableWidgetItem *item = m_Controls.m_LabelSetTableWidget->item(row, 0); int pixelValue = item->data(Qt::UserRole).toInt(); GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetVisible(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetVisible()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } } void QmitkLabelSetWidget::OnColorButtonClicked() { - int row; + int row = -1; for(int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, COLOR_COL)) { row = i; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt(); const mitk::Color &color = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetColor(); QColor initial(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255); QColor qcolor = QColorDialog::getColor(initial, 0, QString("Change color")); if (!qcolor.isValid()) { return; } QPushButton *button = static_cast(m_Controls.m_LabelSetTableWidget->cellWidget(row, COLOR_COL)); if (!button) { return; } button->setAutoFillBackground(true); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(qcolor.red())); styleSheet.append(","); styleSheet.append(QString::number(qcolor.green())); styleSheet.append(","); styleSheet.append(QString::number(qcolor.blue())); styleSheet.append(")"); button->setStyleSheet(styleSheet); mitk::Color newColor; newColor.SetRed(qcolor.red() / 255.0); newColor.SetGreen(qcolor.green() / 255.0); newColor.SetBlue(qcolor.blue() / 255.0); GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetColor(newColor); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } } void QmitkLabelSetWidget::OnRandomColor(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetColor(m_ColorSequenceRainbow.GetNextColor()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::SetOrganColors(const QStringList &organColors) { m_OrganColors = organColors; } void QmitkLabelSetWidget::OnActiveLabelChanged(int pixelValue) { mitk::LabelSetImage *workingImage = GetWorkingImage(); assert(workingImage); workingImage->GetActiveLabelSet()->SetActiveLabel(pixelValue); // MITK_INFO << "Active Label set to << " << pixelValue; mitk::SurfaceBasedInterpolationController *interpolator = mitk::SurfaceBasedInterpolationController::GetInstance(); if (interpolator) { interpolator->SetActiveLabel(pixelValue); } workingImage->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnItemClicked(QTableWidgetItem *item) { if (!item) return; int pixelValue = item->data(Qt::UserRole).toInt(); QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (!ranges.empty() && ranges.back().rowCount() == 1) { SelectLabelByPixelValue(pixelValue); OnActiveLabelChanged(pixelValue); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnItemDoubleClicked(QTableWidgetItem *item) { if (!item) return; int pixelValue = item->data(Qt::UserRole).toInt(); // OnItemClicked(item); <<-- Double click first call OnItemClicked WaitCursorOn(); mitk::LabelSetImage* workingImage = GetWorkingImage(); workingImage->UpdateCenterOfMass(pixelValue, workingImage->GetActiveLayer()); const mitk::Point3D &pos = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates(); WaitCursorOff(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } workingImage->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::SelectLabelByPixelValue(mitk::Label::PixelType pixelValue) { // MITK_INFO << "QmitkLabelSetWidget::SelectLabelByPixelValue " << pixelValue; if (!GetWorkingImage()->ExistLabel(pixelValue)) return; for (int row = 0; row < m_Controls.m_LabelSetTableWidget->rowCount(); row++) { if (m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt() == pixelValue) { m_Controls.m_LabelSetTableWidget->clearSelection(); m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.m_LabelSetTableWidget->selectRow(row); m_Controls.m_LabelSetTableWidget->scrollToItem(m_Controls.m_LabelSetTableWidget->item(row, 0)); m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); // SelectTableWidgetItem(m_Controls.m_LabelSetTableWidget->item(i,0)); // emit resetView(); // GetWorkingImage()->Modified(); return; } } } void QmitkLabelSetWidget::InsertTableWidgetItem(mitk::Label *label) { const mitk::Color &color = label->GetColor(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[1] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[2] * 255)); styleSheet.append(")"); QTableWidget* tableWidget = m_Controls.m_LabelSetTableWidget; int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2; QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); QTableWidgetItem *nameItem = new QTableWidgetItem(text); nameItem->setTextAlignment(Qt::AlignCenter | Qt::AlignLeft); // ---!--- // IMPORTANT: ADD PIXELVALUE TO TABLEWIDGETITEM.DATA nameItem->setData(Qt::UserRole, QVariant(label->GetValue())); // ---!--- QPushButton *pbColor = new QPushButton(tableWidget); pbColor->setFixedSize(24, 24); pbColor->setCheckable(false); pbColor->setAutoFillBackground(true); pbColor->setToolTip("Change label color"); pbColor->setStyleSheet(styleSheet); connect(pbColor, SIGNAL(clicked()), this, SLOT(OnColorButtonClicked())); QPushButton *pbLocked = new QPushButton(tableWidget); pbLocked->setFixedSize(24, 24); QIcon *iconLocked = new QIcon(); iconLocked->addFile(QString::fromUtf8(":/Qmitk/lock.png"), QSize(), QIcon::Normal, QIcon::Off); iconLocked->addFile(QString::fromUtf8(":/Qmitk/unlock.png"), QSize(), QIcon::Normal, QIcon::On); pbLocked->setIcon(*iconLocked); pbLocked->setIconSize(QSize(24, 24)); pbLocked->setCheckable(true); pbLocked->setToolTip("Lock/unlock label"); pbLocked->setChecked(!label->GetLocked()); connect(pbLocked, SIGNAL(clicked()), this, SLOT(OnLockedButtonClicked())); QPushButton *pbVisible = new QPushButton(tableWidget); pbVisible->setFixedSize(24, 24); pbVisible->setAutoRepeat(false); QIcon *iconVisible = new QIcon(); iconVisible->addFile(QString::fromUtf8(":/Qmitk/visible.png"), QSize(), QIcon::Normal, QIcon::Off); iconVisible->addFile(QString::fromUtf8(":/Qmitk/invisible.png"), QSize(), QIcon::Normal, QIcon::On); pbVisible->setIcon(*iconVisible); pbVisible->setIconSize(QSize(24, 24)); pbVisible->setCheckable(true); pbVisible->setToolTip("Show/hide label"); pbVisible->setChecked(!label->GetVisible()); connect(pbVisible, SIGNAL(clicked()), this, SLOT(OnVisibleButtonClicked())); int row = tableWidget->rowCount(); tableWidget->insertRow(row); tableWidget->setRowHeight(row, 24); tableWidget->setItem(row, 0, nameItem); tableWidget->setCellWidget(row, 1, pbLocked); tableWidget->setCellWidget(row, 2, pbColor); tableWidget->setCellWidget(row, 3, pbVisible); tableWidget->selectRow(row); // m_LabelSetImage->SetActiveLabel(label->GetPixelValue()); // m_ToolManager->WorkingDataModified.Send(); // emit activeLabelChanged(label->GetPixelValue()); if (row == 0) { tableWidget->hideRow(row); // hide exterior label } } void QmitkLabelSetWidget::UpdateAllTableWidgetItems() { mitk::LabelSetImage *workingImage = GetWorkingImage(); if (!workingImage) return; // add all labels QTableWidget* tableWidget = m_Controls.m_LabelSetTableWidget; m_LabelStringList.clear(); for(int i = 0 ; i < tableWidget->rowCount(); ++i) { UpdateTableWidgetItem(tableWidget->item(i, 0)); m_LabelStringList.append(tableWidget->item(i, 0)->text()); } OnLabelListModified(m_LabelStringList); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::UpdateTableWidgetItem(QTableWidgetItem *item) { mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Label *label = workingImage->GetLabel(item->data(Qt::UserRole).toInt(), workingImage->GetActiveLayer()); const mitk::Color &color = label->GetColor(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[1] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[2] * 255)); styleSheet.append(")"); QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2; QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); item->setText(text); QPushButton *pbLocked = dynamic_cast(tableWidget->cellWidget(item->row(), 1)); pbLocked->setChecked(!label->GetLocked()); QPushButton *pbColor = dynamic_cast(tableWidget->cellWidget(item->row(), 2)); pbColor->setStyleSheet(styleSheet); QPushButton *pbVisible = dynamic_cast(tableWidget->cellWidget(item->row(), 3)); pbVisible->setChecked(!label->GetVisible()); if (item->row() == 0) { tableWidget->hideRow(item->row()); // hide exterior label } } void QmitkLabelSetWidget::ResetAllTableWidgetItems() { QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; // remove all rows while (tableWidget->rowCount()) { tableWidget->removeRow(0); } mitk::LabelSetImage *workingImage = GetWorkingImage(); if (!workingImage) return; // add all labels m_LabelStringList.clear(); mitk::LabelSet::LabelContainerConstIteratorType it = workingImage->GetActiveLabelSet()->IteratorConstBegin(); mitk::LabelSet::LabelContainerConstIteratorType end = workingImage->GetActiveLabelSet()->IteratorConstEnd(); int pixelValue = -1; while (it != end) { InsertTableWidgetItem(it->second); if (workingImage->GetActiveLabel() == it->second) // get active pixelValue = it->first; m_LabelStringList.append(QString(it->second->GetName().c_str())); it++; } SelectLabelByPixelValue(pixelValue); OnLabelListModified(m_LabelStringList); std::stringstream captionText; captionText << "Number of labels: " << workingImage->GetNumberOfLabels(workingImage->GetActiveLayer()) - 1; m_Controls.m_lblCaption->setText(captionText.str().c_str()); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } int QmitkLabelSetWidget::GetPixelValueOfSelectedItem() { if (m_Controls.m_LabelSetTableWidget->currentItem()) { return m_Controls.m_LabelSetTableWidget->currentItem()->data(Qt::UserRole).toInt(); } return -1; } QStringList &QmitkLabelSetWidget::GetLabelStringList() { return m_LabelStringList; } void QmitkLabelSetWidget::InitializeTableWidget() { QTableWidget *tableWidged = m_Controls.m_LabelSetTableWidget; tableWidged->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); tableWidged->setTabKeyNavigation(false); tableWidged->setAlternatingRowColors(false); tableWidged->setFocusPolicy(Qt::NoFocus); tableWidged->setColumnCount(4); tableWidged->resizeColumnToContents(NAME_COL); tableWidged->setColumnWidth(LOCKED_COL, 25); tableWidged->setColumnWidth(COLOR_COL, 25); tableWidged->setColumnWidth(VISIBLE_COL, 25); #if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) tableWidged->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); #else tableWidged->horizontalHeader()->setResizeMode(0, QHeaderView::Stretch); #endif tableWidged->setContextMenuPolicy(Qt::CustomContextMenu); tableWidged->horizontalHeader()->hide(); tableWidged->setSortingEnabled(false); tableWidged->verticalHeader()->hide(); tableWidged->setEditTriggers(QAbstractItemView::NoEditTriggers); tableWidged->setSelectionMode(QAbstractItemView::ExtendedSelection); tableWidged->setSelectionBehavior(QAbstractItemView::SelectRows); connect(tableWidged, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(OnItemClicked(QTableWidgetItem *))); connect( tableWidged, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this, SLOT(OnItemDoubleClicked(QTableWidgetItem *))); connect(tableWidged, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(OnTableViewContextMenuRequested(const QPoint &))); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(activeLabelChanged(int)), this, SLOT(OnActiveLabelChanged(int)) // ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importSegmentation()), this, SLOT( OnImportSegmentation()) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importLabeledImage()), this, SLOT( OnImportLabeledImage()) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(renameLabel(int, const mitk::Color&, const std::string&)), this, // SLOT(OnRenameLabel(int, const mitk::Color&, const std::string&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createSurface(int, bool)), this, SLOT(OnCreateSurface(int, bool)) // ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(toggleOutline(bool)), this, SLOT(OnToggleOutline(bool)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(goToLabel(const mitk::Point3D&)), this, SIGNAL(goToLabel(const // mitk::Point3D&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateSurface( const QList& // )), // this, SLOT(OnCombineAndCreateSurface( const QList&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createMask(int)), this, SLOT(OnCreateMask(int)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createCroppedMask(int)), this, SLOT(OnCreateCroppedMask(int)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateMask( const QList& // )), // this, SLOT(OnCombineAndCreateMask( const QList&)) ); } void QmitkLabelSetWidget::OnOpacityChanged(int value) { int pixelValue = GetPixelValueOfSelectedItem(); float opacity = static_cast(value) / 100.0f; GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetOpacity(opacity); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } void QmitkLabelSetWidget::setEnabled(bool enabled) { QWidget::setEnabled(enabled); UpdateControls(); } void QmitkLabelSetWidget::SetDataStorage(mitk::DataStorage *storage) { m_DataStorage = storage; } void QmitkLabelSetWidget::OnSearchLabel() { std::string text = m_Controls.m_LabelSearchBox->text().toStdString(); int pixelValue = -1; int row = -1; for(int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if( m_Controls.m_LabelSetTableWidget->item(i, 0)->text().toStdString().compare(text) == 0) { pixelValue = m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt(); row = i; break; } } if (pixelValue == -1) { return; } GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue); QTableWidgetItem *nameItem = m_Controls.m_LabelSetTableWidget->item(row, NAME_COL); if (!nameItem) { return; } m_Controls.m_LabelSetTableWidget->clearSelection(); m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::SingleSelection); m_Controls.m_LabelSetTableWidget->selectRow(row); m_Controls.m_LabelSetTableWidget->scrollToItem(nameItem); m_Controls.m_LabelSetTableWidget->setSelectionMode(QAbstractItemView::ExtendedSelection); GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue); this->WaitCursorOn(); mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates(); m_ToolManager->WorkingDataChanged(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } else { GetWorkingImage()->UpdateCenterOfMass(pixelValue, GetWorkingImage()->GetActiveLayer()); mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates(); emit goToLabel(pos); } this->WaitCursorOff(); } void QmitkLabelSetWidget::OnLabelListModified(const QStringList &list) { QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(list); } mitk::LabelSetImage *QmitkLabelSetWidget::GetWorkingImage() { mitk::DataNode *workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); return workingImage; } mitk::DataNode *QmitkLabelSetWidget::GetWorkingNode() { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); return workingNode; } void QmitkLabelSetWidget::UpdateControls() { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); bool hasWorkingData = (workingNode != nullptr); m_Controls.m_LabelSetTableWidget->setEnabled(hasWorkingData); m_Controls.m_LabelSearchBox->setEnabled(hasWorkingData); if (!hasWorkingData) return; QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(GetLabelStringList()); } void QmitkLabelSetWidget::OnCreateCroppedMask(bool) { m_ToolManager->ActivateTool(-1); mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Image::Pointer maskImage; int pixelValue = GetPixelValueOfSelectedItem(); try { this->WaitCursorOn(); mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New(); cropFilter->SetInput(workingImage->CreateLabelMask(pixelValue)); cropFilter->SetBackgroundValue(0); cropFilter->SetMarginFactor(1.15); cropFilter->Update(); maskImage = cropFilter->GetOutput(); this->WaitCursorOff(); } catch (mitk::Exception &e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } if (maskImage.IsNull()) { QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); name += "-mask"; maskNode->SetName(name); maskNode->SetData(maskImage); maskNode->SetBoolProperty("binary", true); maskNode->SetBoolProperty("outline binary", true); maskNode->SetBoolProperty("outline binary shadow", true); maskNode->SetFloatProperty("outline width", 2.0); maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor()); maskNode->SetOpacity(1.0); m_DataStorage->Add(maskNode, GetWorkingNode()); } void QmitkLabelSetWidget::OnCreateMask(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Image::Pointer maskImage; int pixelValue = GetPixelValueOfSelectedItem(); try { this->WaitCursorOn(); maskImage = workingImage->CreateLabelMask(pixelValue); this->WaitCursorOff(); } catch (mitk::Exception &e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } if (maskImage.IsNull()) { QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); name += "-mask"; maskNode->SetName(name); maskNode->SetData(maskImage); maskNode->SetBoolProperty("binary", true); maskNode->SetBoolProperty("outline binary", true); maskNode->SetBoolProperty("outline binary shadow", true); maskNode->SetFloatProperty("outline width", 2.0); maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor()); maskNode->SetOpacity(1.0); m_DataStorage->Add(maskNode, GetWorkingNode()); } void QmitkLabelSetWidget::OnToggleOutline(bool value) { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); workingNode->SetBoolProperty("labelset.contour.active", value); workingNode->GetData()->Modified(); // fixme: workaround to force data-type rendering (and not only property-type) mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnCreateSmoothedSurface(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::DataNode::Pointer workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); mitk::DataNode::Pointer groupNode = workingNode; surfaceFilter->SetPointerParameter("Group node", groupNode); surfaceFilter->SetPointerParameter("Input", workingImage); surfaceFilter->SetParameter("RequestedLabel", pixelValue); surfaceFilter->SetParameter("Smooth", true); surfaceFilter->SetDataStorage(*m_DataStorage); mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); try { surfaceFilter->StartAlgorithm(); } catch (mitk::Exception &e) { MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); } } void QmitkLabelSetWidget::OnCreateDetailedSurface(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::DataNode::Pointer workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); mitk::DataNode::Pointer groupNode = workingNode; surfaceFilter->SetPointerParameter("Group node", groupNode); surfaceFilter->SetPointerParameter("Input", workingImage); surfaceFilter->SetParameter("RequestedLabel", pixelValue); surfaceFilter->SetParameter("Smooth", false); surfaceFilter->SetDataStorage(*m_DataStorage); mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); try { surfaceFilter->StartAlgorithm(); } catch (mitk::Exception &e) { MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); } } void QmitkLabelSetWidget::OnImportLabeledImage() { /* m_ToolManager->ActivateTool(-1); mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); assert(referenceNode); // Ask the user for a list of files to open QStringList fileNames = QFileDialog::getOpenFileNames( this, "Open Image", m_LastFileOpenPath, mitk::CoreObjectFactory::GetInstance()->GetFileExtensions()); if (fileNames.empty()) return; try { this->WaitCursorOn(); mitk::Image::Pointer image = mitk::IOUtil::LoadImage( fileNames.front().toStdString() ); if (image.IsNull()) { this->WaitCursorOff(); QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation.\n"); return; } mitk::LabelSetImage::Pointer newImage = mitk::LabelSetImage::New(); newImage->InitializeByLabeledImage(image); this->WaitCursorOff(); mitk::DataNode::Pointer newNode = mitk::DataNode::New(); std::string newName = referenceNode->GetName(); newName += "-labels"; newNode->SetName(newName); newNode->SetData(newImage); m_DataStorage->Add(newNode, referenceNode); } catch (mitk::Exception & e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation. See error log for details.\n"); return; } this->UpdateControls(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); */ } void QmitkLabelSetWidget::OnImportSegmentation() { /* m_ToolManager->ActivateTool(-1); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast( workingNode->GetData() ); assert(workingImage); std::string fileExtensions("Segmentation files (*.lset);;"); QString qfileName = QFileDialog::getOpenFileName(this, "Import Segmentation", m_LastFileOpenPath, fileExtensions.c_str() ); if (qfileName.isEmpty() ) return; mitk::NrrdLabelSetImageReader::Pointer reader = mitk::NrrdLabelSetImageReader::New(); reader->SetFileName(qfileName.toLatin1()); try { this->WaitCursorOn(); reader->Update(); mitk::LabelSetImage::Pointer newImage = reader->GetOutput(); workingImage->Concatenate(newImage); this->WaitCursorOff(); } catch ( mitk::Exception& e ) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Import Segmentation", "Could not import the selected segmentation session.\n See error log for details.\n"); } */ mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::WaitCursorOn() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } void QmitkLabelSetWidget::WaitCursorOff() { this->RestoreOverrideCursor(); } void QmitkLabelSetWidget::RestoreOverrideCursor() { QApplication::restoreOverrideCursor(); } void QmitkLabelSetWidget::OnThreadedCalculationDone() { mitk::StatusBar::GetInstance()->Clear(); } diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h index 75a1c706aa..295947c04d 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h +++ b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.h @@ -1,175 +1,175 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkLabelSetWidget_h #define QmitkLabelSetWidget_h #include "MitkSegmentationUIExports.h" #include "mitkColorSequenceRainbow.h" #include "mitkLabel.h" #include "mitkNumericTypes.h" #include class QmitkDataStorageComboBox; class QCompleter; namespace mitk { class LabelSetImage; class LabelSet; class Label; class DataStorage; class ToolManager; class DataNode; } class MITKSEGMENTATIONUI_EXPORT QmitkLabelSetWidget : public QWidget { Q_OBJECT public: explicit QmitkLabelSetWidget(QWidget *parent = nullptr); ~QmitkLabelSetWidget(); void SetDataStorage(mitk::DataStorage *storage); void SetOrganColors(const QStringList &organColors); void UpdateControls(); virtual void setEnabled(bool enabled); QStringList &GetLabelStringList(); signals: /// \brief Send a signal when it was requested to go to a label. void goToLabel(const mitk::Point3D &); void resetView(); public slots: /** * @brief Updates the current labels in the label set widget table. For each label (widget item) the 'UpdateTableWidgetItem' is called. * * Updating means setting the color box of the table, setting the column with and fill it with the label name. * Furthermore the two push buttons for locking and showing/hiding the layer are checked/unchecked. * This functions only changes the appearance of the table widget and no render window update is necessary. */ void UpdateAllTableWidgetItems(); /** * @brief Resets the current labels in the label set widget table. For each label a widget item is inserted into the table. * * Resetting means removing all rows of the widget table and inserting new rows (labels) from the active label set (= layer) of the current working node. * The currently active label is selected and 'Number of labels' is set. * As this function is typically used after one label has been removed or the reference node has been changed (e.g.) the render windows have to be updated. */ void ResetAllTableWidgetItems(); void SelectLabelByPixelValue(mitk::Label::PixelType pixelValue); private slots: // LabelSet dependent void OnOpacityChanged(int); void OnUnlockAllLabels(bool); void OnLockAllLabels(bool); void OnSetAllLabelsVisible(bool); void OnSetAllLabelsInvisible(bool); void OnSetOnlyActiveLabelVisible(bool); void OnRandomColor(bool); void OnRemoveLabel(bool); void OnRemoveLabels(bool); void OnRenameLabel(bool); void OnLockedButtonClicked(); void OnVisibleButtonClicked(); void OnColorButtonClicked(); void OnItemClicked(QTableWidgetItem *item); void OnItemDoubleClicked(QTableWidgetItem *item); void OnTableViewContextMenuRequested(const QPoint &); void InsertTableWidgetItem(mitk::Label *label); void UpdateTableWidgetItem(QTableWidgetItem *item); // reaction to "returnPressed" signal from ... void OnSearchLabel(); // reaction to the button "Change Label" void OnActiveLabelChanged(int pixelValue); // LabelSetImage Dependet void OnCreateDetailedSurface(bool); void OnCreateSmoothedSurface(bool); // reaction to the signal "createMask" from QmitkLabelSetTableWidget void OnCreateMask(bool); void OnCreateMasks(bool); // reaction to the signal "createCroppedMask" from QmitkLabelSetTableWidget void OnCreateCroppedMask(bool); void OnCombineAndCreateMask(bool); void OnCombineAndCreateSurface(bool); void OnEraseLabel(bool); void OnEraseLabels(bool); // reaction to signal "mergeLabel" from QmitkLabelSetTableWidget void OnMergeLabel(bool); void OnMergeLabels(bool); // reaction to the button "Import Segmentation" void OnImportSegmentation(); // reaction to the button "Import Labeled Image" void OnImportLabeledImage(); // reaction to signal "labelListModified" from QmitkLabelSetTableWidget void OnLabelListModified(const QStringList &list); // reaction to the signal "toggleOutline" from QmitkLabelSetTableWidget void OnToggleOutline(bool); private: enum TableColumns { NAME_COL = 0, LOCKED_COL, COLOR_COL, VISIBLE_COL }; void WaitCursorOn(); void WaitCursorOff(); void RestoreOverrideCursor(); void OnThreadedCalculationDone(); void InitializeTableWidget(); int GetPixelValueOfSelectedItem(); mitk::LabelSetImage *GetWorkingImage(); mitk::DataNode *GetWorkingNode(); Ui::QmitkLabelSetWidgetControls m_Controls; mitk::ColorSequenceRainbow m_ColorSequenceRainbow; - QCompleter *m_Completer; - mitk::DataStorage *m_DataStorage; + QCompleter *m_Completer; + mitk::ToolManager *m_ToolManager; QStringList m_OrganColors; QStringList m_LabelStringList; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h index ec7142b8a9..9cdf12dd8d 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h +++ b/Modules/SegmentationUI/Qmitk/QmitkSliceBasedInterpolatorWidget.h @@ -1,202 +1,202 @@ /*=================================================================== 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 QmitkSliceBasedInterpolatorWidget_h_Included #define QmitkSliceBasedInterpolatorWidget_h_Included #include "MitkSegmentationUIExports.h" #include "mitkDataStorage.h" #include "mitkSliceBasedInterpolationController.h" #include #include #include "ui_QmitkSliceBasedInterpolatorWidgetGUIControls.h" namespace mitk { class PlaneGeometry; class SliceNavigationController; class LabelSetImage; class ToolManager; class DiffSliceOperation; } /** \brief GUI for slices interpolation. \ingroup ToolManagerEtAl \ingroup Widgets \sa QmitkInteractiveSegmentation \sa mitk::SegmentationInterpolation There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage While mitk::SegmentationInterpolation does the bookkeeping of interpolation (keeping track of which slices contain how much segmentation) and the algorithmic work, QmitkSliceBasedInterpolatorWidget is responsible to watch the GUI, to notice, which slice is currently visible. It triggers generation of interpolation suggestions and also triggers acception of suggestions. \todo show/hide feedback on demand Last contributor: $Author: maleike $ */ class MITKSEGMENTATIONUI_EXPORT QmitkSliceBasedInterpolatorWidget : public QWidget { Q_OBJECT public: QmitkSliceBasedInterpolatorWidget(QWidget *parent = 0, const char *name = 0); virtual ~QmitkSliceBasedInterpolatorWidget(); void SetDataStorage(mitk::DataStorage &storage); /** Sets the slice navigation controllers for getting slice changed events from the views. */ void SetSliceNavigationControllers(const QList &controllers); void OnToolManagerWorkingDataModified(); void OnTimeChanged(itk::Object *sender, const itk::EventObject &); void OnSliceChanged(itk::Object *sender, const itk::EventObject &); void OnSliceNavigationControllerDeleted(const itk::Object *sender, const itk::EventObject &); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnSliceInterpolationInfoChanged(const itk::EventObject &); Ui::QmitkSliceBasedInterpolatorWidgetGUIControls m_Controls; signals: void signalSliceBasedInterpolationEnabled(bool); public slots: /** \brief Reaction to "Start/Stop" button click */ void OnToggleWidgetActivation(bool); protected slots: /** \brief Reaction to "Accept Current Slice" button click. */ void OnAcceptInterpolationClicked(); /* \brief Reaction to "Accept All Slices" button click. Opens popup to ask about which orientation should be interpolated */ void OnAcceptAllInterpolationsClicked(); /* \brief Called from popup menu of OnAcceptAllInterpolationsClicked() Will trigger interpolation for all slices in given orientation */ void OnAcceptAllPopupActivated(QAction *action); protected: typedef std::map ActionToSliceDimensionMapType; const ActionToSliceDimensionMapType CreateActionToSliceDimension(); ActionToSliceDimensionMapType m_ActionToSliceDimensionMap; void AcceptAllInterpolations(mitk::SliceNavigationController *slicer); void WaitCursorOn(); void WaitCursorOff(); void RestoreOverrideCursor(); /** Gets the working slice based on the given plane geometry and last saved interaction \param planeGeometry a plane geometry */ mitk::Image::Pointer GetWorkingSlice(const mitk::PlaneGeometry *planeGeometry); /** Retrieves the currently selected PlaneGeometry from a SlicedGeometry3D that is generated by a SliceNavigationController and calls Interpolate to further process this PlaneGeometry into an interpolation. \param e is a actually a mitk::SliceNavigationController::GeometrySliceEvent, sent by a SliceNavigationController \param slice the SliceNavigationController */ void TranslateAndInterpolateChangedSlice(const itk::EventObject &e, mitk::SliceNavigationController *slicer); /** Given a PlaneGeometry, this method figures out which slice of the first working image (of the associated ToolManager) should be interpolated. The actual work is then done by our SegmentationInterpolation object. */ void Interpolate(mitk::PlaneGeometry *plane, unsigned int timeStep, mitk::SliceNavigationController *slicer); /** Called internally to update the interpolation suggestion. Finds out about the focused render window and requests an interpolation. */ void UpdateVisibleSuggestion(); private: mitk::SliceBasedInterpolationController::Pointer m_SliceInterpolatorController; mitk::ToolManager *m_ToolManager; bool m_Activated; template void WritePreviewOnWorkingImage(itk::Image *target, const mitk::Image *source, int overwritevalue); QHash m_ControllerToTimeObserverTag; QHash m_ControllerToSliceObserverTag; QHash m_ControllerToDeleteObserverTag; unsigned int m_InterpolationInfoChangedObserverTag; mitk::DiffSliceOperation *m_doOperation; mitk::DiffSliceOperation *m_undoOperation; mitk::DataNode::Pointer m_PreviewNode; mitk::Image::Pointer m_PreviewImage; mitk::LabelSetImage::Pointer m_WorkingImage; - mitk::SliceNavigationController *m_LastSNC; - - unsigned int m_LastSliceIndex; - QHash m_TimeStep; mitk::DataStorage::Pointer m_DataStorage; + + mitk::SliceNavigationController *m_LastSNC; + + unsigned int m_LastSliceIndex; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp index 64cea6d27a..38ac3d9728 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp @@ -1,360 +1,360 @@ /*=================================================================== 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 "QmitkSurfaceBasedInterpolatorWidget.h" #include "mitkColorProperty.h" #include "mitkInteractionConst.h" #include "mitkOperationEvent.h" #include "mitkProgressBar.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkSegTool2D.h" #include "mitkSliceNavigationController.h" #include "mitkSurfaceToImageFilter.h" #include "mitkUndoController.h" #include "mitkVtkRepresentationProperty.h" #include #include "QmitkStdMultiWidget.h" #include #include #include QmitkSurfaceBasedInterpolatorWidget::QmitkSurfaceBasedInterpolatorWidget(QWidget *parent, const char * /*name*/) : QWidget(parent), m_SurfaceBasedInterpolatorController(mitk::SurfaceBasedInterpolationController::GetInstance()), m_ToolManager(nullptr), - m_DataStorage(nullptr), - m_Activated(false) + m_Activated(false), + m_DataStorage(nullptr) { m_Controls.setupUi(this); m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); Q_ASSERT(m_ToolManager); m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &QmitkSurfaceBasedInterpolatorWidget::OnToolManagerWorkingDataModified); connect(m_Controls.m_btStart, SIGNAL(toggled(bool)), this, SLOT(OnToggleWidgetActivation(bool))); connect(m_Controls.m_btAccept, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked())); connect(m_Controls.m_cbShowPositionNodes, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool))); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSurfaceBasedInterpolatorWidget::OnSurfaceInterpolationInfoChanged); m_SurfaceInterpolationInfoChangedObserverTag = m_SurfaceBasedInterpolatorController->AddObserver(itk::ModifiedEvent(), command); m_InterpolatedSurfaceNode = mitk::DataNode::New(); m_InterpolatedSurfaceNode->SetName("Surface Interpolation feedback"); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0, 255.0, 0.0)); m_InterpolatedSurfaceNode->SetProperty("opacity", mitk::FloatProperty::New(0.5)); m_InterpolatedSurfaceNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_InterpolatedSurfaceNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode = mitk::DataNode::New(); m_3DContourNode->SetName("Drawn Contours"); m_3DContourNode->SetProperty("color", mitk::ColorProperty::New(0.0, 0.0, 0.0)); m_3DContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f)); m_3DContourNode->SetProperty("includeInBoundingBox", mitk::BoolProperty::New(false)); m_3DContourNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); m_3DContourNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); m_3DContourNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); m_3DContourNode->SetVisibility( false, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(OnSurfaceInterpolationFinished())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer())); m_Timer = new QTimer(this); connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor())); m_Controls.m_btAccept->setEnabled(false); m_Controls.m_cbShowPositionNodes->setEnabled(false); this->setEnabled(false); } void QmitkSurfaceBasedInterpolatorWidget::SetDataStorage(mitk::DataStorage &storage) { m_DataStorage = &storage; } QmitkSurfaceBasedInterpolatorWidget::~QmitkSurfaceBasedInterpolatorWidget() { m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate( this, &QmitkSurfaceBasedInterpolatorWidget::OnToolManagerWorkingDataModified); if (m_DataStorage->Exists(m_3DContourNode)) m_DataStorage->Remove(m_3DContourNode); if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) m_DataStorage->Remove(m_InterpolatedSurfaceNode); // remove observer m_SurfaceBasedInterpolatorController->RemoveObserver(m_SurfaceInterpolationInfoChangedObserverTag); delete m_Timer; } void QmitkSurfaceBasedInterpolatorWidget::ShowInterpolationResult(bool status) { if (m_InterpolatedSurfaceNode.IsNotNull()) m_InterpolatedSurfaceNode->SetVisibility(status); if (m_3DContourNode.IsNotNull()) m_3DContourNode->SetVisibility( status, mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSurfaceBasedInterpolatorWidget::OnSurfaceInterpolationFinished() { mitk::Surface::Pointer interpolatedSurface = m_SurfaceBasedInterpolatorController->GetInterpolationResult(); if (interpolatedSurface.IsNotNull()) { m_InterpolatedSurfaceNode->SetData(interpolatedSurface); m_3DContourNode->SetData(m_SurfaceBasedInterpolatorController->GetContoursAsSurface()); this->ShowInterpolationResult(true); } else { m_InterpolatedSurfaceNode->SetData(nullptr); m_3DContourNode->SetData(nullptr); this->ShowInterpolationResult(false); } } void QmitkSurfaceBasedInterpolatorWidget::OnShowMarkers(bool state) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state)); } mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_ToolManager->GetTools().size(); for (unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_ToolManager->GetToolById(i)); if (manualSegmentationTool) { manualSegmentationTool->SetShowMarkerNodes(state); } } } void QmitkSurfaceBasedInterpolatorWidget::StartUpdateInterpolationTimer() { m_Timer->start(500); } void QmitkSurfaceBasedInterpolatorWidget::StopUpdateInterpolationTimer() { m_Timer->stop(); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0, 255.0, 0.0)); mitk::RenderingManager::GetInstance()->RequestUpdate( mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSurfaceBasedInterpolatorWidget::ChangeSurfaceColor() { float currentColor[3]; m_InterpolatedSurfaceNode->GetColor(currentColor); float yellow[3] = {255.0, 255.0, 0.0}; if (currentColor[2] == yellow[2]) { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0, 255.0, 255.0)); } else { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(yellow)); } m_InterpolatedSurfaceNode->Update(); mitk::RenderingManager::GetInstance()->RequestUpdate( mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSurfaceBasedInterpolatorWidget::OnToolManagerWorkingDataModified() { mitk::DataNode *workingNode = this->m_ToolManager->GetWorkingData(0); if (!workingNode) { this->setEnabled(false); return; } mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); // TODO adapt tool manager so that this check is done there, e.g. convenience function // Q_ASSERT(workingImage); if (!workingImage) { this->setEnabled(false); return; } if (workingImage->GetDimension() > 4 || workingImage->GetDimension() < 3) { this->setEnabled(false); return; } m_WorkingImage = workingImage; this->setEnabled(true); } void QmitkSurfaceBasedInterpolatorWidget::OnRunInterpolation() { m_SurfaceBasedInterpolatorController->Interpolate(); } void QmitkSurfaceBasedInterpolatorWidget::OnToggleWidgetActivation(bool enabled) { Q_ASSERT(m_ToolManager); mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); if (!workingNode) return; m_Controls.m_btAccept->setEnabled(enabled); m_Controls.m_cbShowPositionNodes->setEnabled(enabled); if (enabled) m_Controls.m_btStart->setText("Stop"); else m_Controls.m_btStart->setText("Start"); for (unsigned int i = 0; i < m_ToolManager->GetTools().size(); i++) { mitk::SegTool2D *tool = dynamic_cast(m_ToolManager->GetToolById(i)); if (tool) tool->SetEnable3DInterpolation(enabled); } if (enabled) { if (!m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { m_DataStorage->Add(m_InterpolatedSurfaceNode); } if (!m_DataStorage->Exists(m_3DContourNode)) { m_DataStorage->Add(m_3DContourNode); } mitk::Vector3D spacing = m_WorkingImage->GetGeometry(0)->GetSpacing(); double minSpacing(100); double maxSpacing(0); for (int i = 0; i < 3; i++) { if (spacing[i] < minSpacing) { minSpacing = spacing[i]; } else if (spacing[i] > maxSpacing) { maxSpacing = spacing[i]; } } m_SurfaceBasedInterpolatorController->SetWorkingImage(m_WorkingImage); m_SurfaceBasedInterpolatorController->SetActiveLabel(m_WorkingImage->GetActiveLabel()->GetValue()); m_SurfaceBasedInterpolatorController->SetMaxSpacing(maxSpacing); m_SurfaceBasedInterpolatorController->SetMinSpacing(minSpacing); m_SurfaceBasedInterpolatorController->SetDistanceImageVolume(50000); int ret = QMessageBox::Yes; if (m_SurfaceBasedInterpolatorController->EstimatePortionOfNeededMemory() > 0.5) { QMessageBox msgBox; msgBox.setText("Due to short handed system memory the 3D interpolation may be very slow!"); msgBox.setInformativeText("Are you sure you want to activate the 3D interpolation?"); msgBox.setStandardButtons(QMessageBox::No | QMessageBox::Yes); ret = msgBox.exec(); } if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); if (ret == QMessageBox::Yes) { m_Future = QtConcurrent::run(this, &QmitkSurfaceBasedInterpolatorWidget::OnRunInterpolation); m_Watcher.setFuture(m_Future); } } else { if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { m_DataStorage->Remove(m_InterpolatedSurfaceNode); } if (m_DataStorage->Exists(m_3DContourNode)) { m_DataStorage->Remove(m_3DContourNode); } mitk::UndoController::GetCurrentUndoModel()->Clear(); } m_Activated = enabled; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSurfaceBasedInterpolatorWidget::OnAcceptInterpolationClicked() { if (m_InterpolatedSurfaceNode.IsNotNull() && m_InterpolatedSurfaceNode->GetData()) { // m_WorkingImage->SurfaceStamp(dynamic_cast(m_InterpolatedSurfaceNode->GetData()), false); this->ShowInterpolationResult(false); } } void QmitkSurfaceBasedInterpolatorWidget::OnSurfaceInterpolationInfoChanged(const itk::EventObject & /*e*/) { if (m_Activated) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSurfaceBasedInterpolatorWidget::OnRunInterpolation); m_Watcher.setFuture(m_Future); } } diff --git a/Modules/TubeGraph/include/mitkTubeGraphVtkMapper3D.h b/Modules/TubeGraph/include/mitkTubeGraphVtkMapper3D.h index bef19cb3eb..4ba43eee9e 100644 --- a/Modules/TubeGraph/include/mitkTubeGraphVtkMapper3D.h +++ b/Modules/TubeGraph/include/mitkTubeGraphVtkMapper3D.h @@ -1,120 +1,120 @@ /*=================================================================== 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 TubeGraphVtkMapper3D_H_HEADER_INCLUDED #define TubeGraphVtkMapper3D_H_HEADER_INCLUDED #include #include #include "mitkCircularProfileTubeElement.h" #include "mitkTubeGraph.h" #include "mitkTubeGraphProperty.h" -#include "mitkVtkMapper3D.h" +#include "mitkVtkMapper.h" #include #include #include #include #include namespace mitk { /** * 3D Mapper for mitk::Graph< TubeGraphVertex, TubeGraphEdge >. This mapper creates tubes * around each tubular structure by using vtkTubeFilter. * */ - class MITKTUBEGRAPH_EXPORT TubeGraphVtkMapper3D : public VtkMapper3D + class MITKTUBEGRAPH_EXPORT TubeGraphVtkMapper3D : public VtkMapper { public: /* Typedefs */ typedef TubeGraph::EdgeDescriptorType EdgeDescriptorType; typedef TubeGraph::VertexDescriptorType VertexDescriptorType; - mitkClassMacro(TubeGraphVtkMapper3D, VtkMapper3D); + mitkClassMacro(TubeGraphVtkMapper3D, VtkMapper); itkNewMacro(Self); /** * Returns the input data object of the given filter. In this * case, a mitk::Graph< TubeGraphVertex, TubeGraphEdge > is returned. */ virtual const TubeGraph *GetInput(); virtual vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; protected: TubeGraphVtkMapper3D(); virtual ~TubeGraphVtkMapper3D(); /** * This method is called, each time a specific renderer is updated. */ virtual void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; /** * Generate vtkPolyData containing the tube centerlines and * sets these as input for a vtkTubeFilter, which generates tubes * around the edges. Also generates vtkActors for each vertex. Here * the vtkPolyData are vtkSphereSources. All poly data will be clipped * with each other on an furcation. So you get end-caps and connecting * pieces from the spheres. Clipping the tubes with each other avoids * structures within the general view. */ virtual void GenerateTubeGraphData(mitk::BaseRenderer *renderer); /** * Render only the visual information like color or visibility new. */ virtual void RenderTubeGraphPropertyInformation(mitk::BaseRenderer *renderer); /** * Converts a single tube into a vtkPolyData. Each point of the * tube surface is labeled with the tube id. */ void GeneratePolyDataForTube(TubeGraphEdge &edge, const TubeGraph::Pointer &graph, const TubeGraphProperty::Pointer &graphProperty, mitk::BaseRenderer *renderer); void GeneratePolyDataForFurcation(TubeGraphVertex &vertex, const TubeGraph::Pointer &graph, mitk::BaseRenderer *renderer); void ClipPolyData(TubeGraphVertex &vertex, const TubeGraph::Pointer &graph, const TubeGraphProperty::Pointer &graphProperty, mitk::BaseRenderer *renderer); private: bool ClipStructures(); class LocalStorage : public mitk::Mapper::BaseLocalStorage { public: vtkSmartPointer m_vtkTubeGraphAssembly; std::map> m_vtkTubesActorMap; std::map> m_vtkSpheresActorMap; itk::TimeStamp m_lastGenerateDataTime; itk::TimeStamp m_lastRenderDataTime; LocalStorage() { m_vtkTubeGraphAssembly = vtkSmartPointer::New(); } ~LocalStorage() {} }; LocalStorageHandler m_LSH; }; } // namespace #endif diff --git a/Modules/TubeGraph/include/mitkUndirectedGraph.h b/Modules/TubeGraph/include/mitkUndirectedGraph.h index 27f90adffe..85ba99cba0 100644 --- a/Modules/TubeGraph/include/mitkUndirectedGraph.h +++ b/Modules/TubeGraph/include/mitkUndirectedGraph.h @@ -1,178 +1,177 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef _MITK_UndirectedGraph_H #define _MITK_UndirectedGraph_H #include #ifndef Q_MOC_RUN #include #endif /* definition of basic boost::graph properties */ enum vertex_properties_t { vertex_properties }; enum edge_properties_t { edge_properties }; namespace boost { BOOST_INSTALL_PROPERTY(vertex, properties); BOOST_INSTALL_PROPERTY(edge, properties); } namespace mitk { /** * \brief Template class for undirected graphs.Paramters should be the vertex and edge classes, which contains the * information. */ template class UndirectedGraph : public BaseData { public: //--- Typedefs ---// typedef TVertex VertexType; typedef TEdge EdgeType; /** * Creating the graph type * listS: Represents the OutEdgeList as a std::list * vecS: Represents the VertexList as a std::vector * undirectedS: Representation for an undirected graph * VertexProperty: Defines that all vertex are represented by VertexType * EdgeProperty: Defines that all edges are represented by EdgeType */ typedef boost::adjacency_list, boost::property> GraphType; /* a bunch of graph-specific typedefs */ typedef typename boost::graph_traits::vertex_descriptor VertexDescriptorType; typedef typename boost::graph_traits::edge_descriptor EdgeDescriptorType; typedef typename boost::graph_traits::vertex_iterator VertexIteratorType; typedef typename boost::graph_traits::edge_iterator EdgeIteratorType; typedef typename boost::graph_traits::adjacency_iterator AdjacenyIteratorType; typedef typename boost::graph_traits::out_edge_iterator OutEdgeIteratorType; typedef typename boost::graph_traits::in_edge_iterator InEdgeIteratorType; //--- Macros ---// mitkClassMacro(UndirectedGraph, BaseData); itkNewMacro(Self); // virtual methods that need to be implemented virtual void UpdateOutputInformation() override { if (this->GetSource()) this->GetSource()->UpdateOutputInformation(); } virtual void SetRequestedRegionToLargestPossibleRegion() override {} virtual bool RequestedRegionIsOutsideOfTheBufferedRegion() override { return false; } virtual bool VerifyRequestedRegion() override { return true; } - virtual void SetRequestedRegion(const itk::DataObject *data) override {} + virtual void SetRequestedRegion(const itk::DataObject *) override {} /** Add a new vertex to the graph */ VertexDescriptorType AddVertex(const VertexType &vertexData); /** Remove the vertex from the graph */ void RemoveVertex(const VertexDescriptorType &vertex); /** Get the vertex data of the given vertex descriptor */ VertexType GetVertex(const VertexDescriptorType &vertex); /** Set the vertex data of the given vertex descriptor */ void SetVertex(const VertexDescriptorType &vertex, const VertexType &vertexData); /**Returns the descriptor if the vertex exist in the graph, otherwise undefined*/ VertexDescriptorType GetVertexDescriptor(const VertexType &vertexData) const; /** Add a new edge between the given vertices to the graph */ EdgeDescriptorType AddEdge(const VertexDescriptorType &vertexA, const VertexDescriptorType &vertexB, const EdgeType &edgeData); /** Remove the edge from the graph */ void RemoveEdge(const EdgeDescriptorType &edge); /** Get the edge data of the given edge descriptor */ EdgeType GetEdge(const EdgeDescriptorType &edge); /** Set the edge data of the given edge descriptor */ void SetEdge(const EdgeDescriptorType &edge, const EdgeType &edgeData); /**Returns the descriptor if the edge exist in the graph, otherwise undefined*/ EdgeDescriptorType GetEdgeDescriptor(const EdgeType &edgeData) const; /** Get parent and target vertex of the given edge*/ std::pair GetVerticesOfAnEdge(const EdgeDescriptorType &edge) const; /** Returns the edge descriptor from the edge which has the given vertices as source and target */ EdgeDescriptorType GetEdgeDescriptorByVerices(const VertexDescriptorType &vertexA, const VertexDescriptorType &vertexB) const; /** Get all edges of the given vertex */ std::vector GetAllEdgesOfAVertex(const VertexDescriptorType &vertex) const; /** Get overall number of vertices in the graph */ int GetNumberOfVertices() const; /** get overall number of edges in the graph */ int GetNumberOfEdges() const; /** get vector containing all the vertices of the graph*/ std::vector GetVectorOfAllVertices() const; /** get vector containing all the edges of the network */ std::vector GetVectorOfAllEdges() const; /** clear the graph */ void Clear() override; /** get the graph */ const GraphType &GetGraph() const; UndirectedGraph &operator=(const UndirectedGraph &rhs); protected: UndirectedGraph(); - UndirectedGraph(const UndirectedGraph &graph); virtual ~UndirectedGraph(); GraphType m_Graph; private: /** property access for vertices */ VertexType &properties(const VertexDescriptorType &vertex); /** property access for vertices */ const VertexType &properties(const VertexDescriptorType &vertex) const; /** property access for edges */ EdgeType &properties(const EdgeDescriptorType &edge); /** property access for edges */ const EdgeType &properties(const EdgeDescriptorType &edge) const; }; } // namespace mitk #include "mitkUndirectedGraph.txx" #endif /* _MITK_UndirectedGraph_H */ diff --git a/Modules/TubeGraph/include/mitkUndirectedGraph.txx b/Modules/TubeGraph/include/mitkUndirectedGraph.txx index cb36149af2..fbfe84222a 100644 --- a/Modules/TubeGraph/include/mitkUndirectedGraph.txx +++ b/Modules/TubeGraph/include/mitkUndirectedGraph.txx @@ -1,346 +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 _mitkUndirectedGraph_txx #define _mitkUndirectedGraph_txx namespace mitk { template - UndirectedGraph::UndirectedGraph() : m_Graph(NULL) - { - } - - template - UndirectedGraph::UndirectedGraph(const UndirectedGraph &graph) - : m_Graph(graph.m_Graph) + UndirectedGraph::UndirectedGraph() { } template UndirectedGraph::~UndirectedGraph() { } template typename UndirectedGraph::VertexDescriptorType UndirectedGraph::AddVertex( const typename UndirectedGraph::VertexType &vertexData) { VertexDescriptorType vertex = boost::add_vertex(m_Graph); this->properties(vertex) = vertexData; this->Modified(); return vertex; } template void UndirectedGraph::RemoveVertex( const typename UndirectedGraph::VertexDescriptorType &vertex) { boost::clear_vertex(vertex, m_Graph); boost::remove_vertex(vertex, m_Graph); this->Modified(); } template typename UndirectedGraph::VertexType UndirectedGraph::GetVertex( const typename UndirectedGraph::VertexDescriptorType &vertex) { return this->properties(vertex); } template void UndirectedGraph::SetVertex( const typename UndirectedGraph::VertexDescriptorType &vertex, const typename UndirectedGraph::VertexType &vertexData) { this->properties(vertex) = vertexData; this->Modified(); } template typename UndirectedGraph::VertexDescriptorType UndirectedGraph::GetVertexDescriptor( const typename UndirectedGraph::VertexType &vertexData) const { typename UndirectedGraph::VertexIteratorType iterator, end; boost::tie(iterator, end) = boost::vertices(m_Graph); for (; iterator != end; ++iterator) { typename UndirectedGraph::VertexType tempVertex; // the value of an iterator is a descriptor tempVertex = this->properties(*iterator); if (vertexData == tempVertex) return *iterator; } MITK_ERROR << "Error in mitkUndirectedGraph::GetVertexDescriptor(...): vertex could not be found." << std::endl; throw std::runtime_error( "Exception thrown in mitkUndirectedGraph::GetVertexDescriptor(...): vertex could not be found."); } template typename UndirectedGraph::EdgeDescriptorType UndirectedGraph::AddEdge( const typename UndirectedGraph::VertexDescriptorType &vertexA, const typename UndirectedGraph::VertexDescriptorType &vertexB, const typename UndirectedGraph::EdgeType &edgeData) { EdgeDescriptorType edge; bool inserted = false; boost::tie(edge, inserted) = boost::add_edge(vertexA, vertexB, m_Graph); if (!inserted) { MITK_ERROR << "Error in mitkUndirectedGraph::addEdge(...): edge could not be inserted." << std::endl; throw std::runtime_error("Exception thrown in mitkUndirectedGraph::addEdge(...): edge could not be inserted."); } this->properties(edge) = edgeData; this->Modified(); return edge; } template void UndirectedGraph::RemoveEdge( const typename UndirectedGraph::EdgeDescriptorType &edge) { boost::remove_edge(edge, m_Graph); this->Modified(); } template typename UndirectedGraph::EdgeType UndirectedGraph::GetEdge( const typename UndirectedGraph::EdgeDescriptorType &edge) { return this->properties(edge); } template void UndirectedGraph::SetEdge( const typename UndirectedGraph::EdgeDescriptorType &edge, const typename UndirectedGraph::EdgeType &edgeData) { this->properties(edge) = edgeData; this->Modified(); } template typename UndirectedGraph::EdgeDescriptorType UndirectedGraph::GetEdgeDescriptor( const typename UndirectedGraph::EdgeType &edgeData) const { typename UndirectedGraph::EdgeIteratorType iterator, end; boost::tie(iterator, end) = boost::edges(m_Graph); for (; iterator != end; ++iterator) { typename UndirectedGraph::EdgeType tempEdge; // the value of an iterator is a descriptor tempEdge = this->properties(*iterator); if (edgeData == tempEdge) return iterator.dereference(); } // if no edge match, throw error MITK_ERROR << "Error in mitkUndirectedGraph::GetEdgeDescriptor(...): edge could not be found." << std::endl; throw std::runtime_error( "Exception thrown in mitkUndirectedGraph::GetEdgeDescriptor(...): edge could not be found."); } template std::pair::VertexType, typename UndirectedGraph::VertexType> UndirectedGraph::GetVerticesOfAnEdge( const typename UndirectedGraph::EdgeDescriptorType &edge) const { typename UndirectedGraph::VertexType sourceNode, targetNode; sourceNode = this->properties(boost::source(edge, m_Graph)); targetNode = this->properties(boost::target(edge, m_Graph)); std::pair::VertexType, typename UndirectedGraph::VertexType> nodePair(sourceNode, targetNode); return nodePair; } template typename UndirectedGraph::EdgeDescriptorType UndirectedGraph::GetEdgeDescriptorByVerices( const typename UndirectedGraph::VertexDescriptorType &vertexA, const typename UndirectedGraph::VertexDescriptorType &vertexB) const { typename UndirectedGraph::EdgeDescriptorType edge; bool edgeExists(false); boost::tie(edge, edgeExists) = boost::edge(vertexA, vertexB, m_Graph); if (edgeExists) return edge; else { MITK_ERROR << "Error in mitkUndirectedGraph::GetEdgeDescriptorByVerices(...): edge could not be found." << std::endl; throw std::runtime_error( "Exception thrown in mitkUndirectedGraph::GetEdgeDescriptorByVerices(...): edge could not be found."); } } template std::vector::EdgeType> UndirectedGraph::GetAllEdgesOfAVertex( const typename UndirectedGraph::VertexDescriptorType &vertex) const { typename UndirectedGraph::OutEdgeIteratorType iterator, end; boost::tie(iterator, end) = boost::out_edges(vertex, m_Graph); std::vector::EdgeType> vectorOfEdges; for (; iterator != end; ++iterator) { typename UndirectedGraph::EdgeType tempEdge; // the value of an iterator is a descriptor tempEdge = this->properties(*iterator); vectorOfEdges.push_back(tempEdge); } return vectorOfEdges; } template int UndirectedGraph::GetNumberOfVertices() const { // Returns the number of vertices in the graph return boost::num_vertices(m_Graph); } template int UndirectedGraph::GetNumberOfEdges() const { // Returns the number of edges in the graph return boost::num_edges(m_Graph); } template std::vector::VertexType> UndirectedGraph::GetVectorOfAllVertices() const { typename UndirectedGraph::VertexIteratorType iterator, end; // sets iterator to start end end to end boost::tie(iterator, end) = boost::vertices(m_Graph); std::vector::VertexType> vectorOfNodes; for (; iterator != end; ++iterator) { typename UndirectedGraph::VertexType tempVertex; // the value of an iterator is a descriptor tempVertex = this->properties(*iterator); vectorOfNodes.push_back(tempVertex); } return vectorOfNodes; } template std::vector::EdgeType> UndirectedGraph::GetVectorOfAllEdges() const { typename UndirectedGraph::EdgeIteratorType iterator, end; // sets iterator to start end end to end boost::tie(iterator, end) = boost::edges(m_Graph); std::vector::EdgeType> vectorOfEdges; for (; iterator != end; ++iterator) { typename UndirectedGraph::EdgeType tempEdge; // the value of an iterator is a descriptor tempEdge = this->properties(*iterator); vectorOfEdges.push_back(tempEdge); } return vectorOfEdges; } template void UndirectedGraph::Clear() { m_Graph.clear(); this->Modified(); } template const typename UndirectedGraph::GraphType &UndirectedGraph::GetGraph() const { return m_Graph; } /* operators */ template UndirectedGraph &UndirectedGraph::operator=( const UndirectedGraph &rhs) { m_Graph = rhs.m_Graph; return *this; } template TVertex &UndirectedGraph::properties( const typename UndirectedGraph::VertexDescriptorType &vertex) { typename boost::property_map::type param = boost::get(vertex_properties, m_Graph); return param[vertex]; } template const TVertex &UndirectedGraph::properties( const typename UndirectedGraph::VertexDescriptorType &vertex) const { typename boost::property_map::const_type param = boost::get(vertex_properties, m_Graph); return param[vertex]; } template TEdge &UndirectedGraph::properties( const typename UndirectedGraph::EdgeDescriptorType &edge) { typename boost::property_map::type param = boost::get(edge_properties, m_Graph); return param[edge]; } template const TEdge &UndirectedGraph::properties( const typename UndirectedGraph::EdgeDescriptorType &edge) const { typename boost::property_map::const_type param = boost::get(edge_properties, m_Graph); return param[edge]; } } // namespace mitk #endif //_mitkUndirectedGraph_txx diff --git a/Modules/TubeGraph/src/DataStructure/mitkTubeGraph.cpp b/Modules/TubeGraph/src/DataStructure/mitkTubeGraph.cpp index 8ae4786625..b7c5f4647e 100644 --- a/Modules/TubeGraph/src/DataStructure/mitkTubeGraph.cpp +++ b/Modules/TubeGraph/src/DataStructure/mitkTubeGraph.cpp @@ -1,300 +1,276 @@ /*=================================================================== 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 "mitkTubeGraph.h" #include "mitkGeometry3D.h" const mitk::TubeGraph::TubeDescriptorType mitk::TubeGraph::ErrorId = std::pair(boost::graph_traits::null_vertex(), boost::graph_traits::null_vertex()); mitk::TubeGraph::TubeGraph() { } mitk::TubeGraph::TubeGraph(const mitk::TubeGraph &graph) : UndirectedGraph(graph) { } mitk::TubeGraph::~TubeGraph() { } std::vector mitk::TubeGraph::SearchShortestPath( - const TubeDescriptorType &startTube, const TubeDescriptorType &endTube /*, std::vector barrier*/) + const TubeDescriptorType &, const TubeDescriptorType &) { std::vector shortestPath; - /* - EdgeDescriptorType startEdge = boost::edge(startTube.first, startTube.second, m_Graph).first; - - std::vector< DescriptorType > predecessorMap( boost::num_vertices( *boostGraph ) ); - int numberOfNodes( boost::num_vertices( *boostGraph ) ); - - m_DistanceMatrix.resize( numberOfNodes ); - for( int index(0); index < m_DistanceMatrix.size(); index++ ) - { - m_DistanceMatrix[ index ].resize( numberOfNodes ); - } - - IteratorType iterator, end; - boost::tie(iterator, end) = boost::vertices( *boostGraph ); - - for ( int index(0) ; iterator != end; ++iterator, index++) - { - boost::dijkstra_shortest_paths(*boostGraph, *iterator, boost::predecessor_map(&predecessorMap[ 0 - ]).distance_map(&m_DistanceMatrix[ index ][ 0 ]).weight_map( boost::get( - &mitk::ConnectomicsNetwork::NetworkEdge::edge_weight ,*boostGraph ) ) ) ; - } - - - */ return shortestPath; } std::vector mitk::TubeGraph::SearchAllPathBetweenVertices( const mitk::TubeGraph::TubeDescriptorType &startTube, const mitk::TubeGraph::TubeDescriptorType &endTube /*, std::vector barrier*/) { // http://lists.boost.org/boost-users/att-9001/maze.cpp // http://www.boost.org/doc/libs/1_49_0/libs/graph/example/bfs.cpp typedef std::map EdgeMap; typedef boost::associative_property_map PredecessorMap; typedef boost::edge_predecessor_recorder PredecessorVisitor; typedef boost::dfs_visitor> DFSVisitor; EdgeMap edgesMap; PredecessorMap predecessorMap(edgesMap); PredecessorVisitor predecessorVisitor(predecessorMap); boost::null_visitor nullVisitor; DFSVisitor visitor = boost::make_dfs_visitor(std::make_pair(predecessorVisitor, nullVisitor)); std::map vertexColorMap; std::map edgeColorMap; boost::undirected_dfs( m_Graph, visitor, make_assoc_property_map(vertexColorMap), make_assoc_property_map(edgeColorMap), startTube.second); std::vector solutionPath; solutionPath.push_back(endTube); VertexDescriptorType pathEdgeSource = endTube.first; VertexDescriptorType pathEdgeTarget; MITK_INFO << "Source: [" << startTube.first << "," << startTube.second << "] Target: [" << endTube.first << "," << endTube.second << "]"; MITK_INFO << "tube [" << endTube.first << "," << endTube.second << "]"; do { if (pathEdgeSource == 10393696) break; EdgeDescriptorType edge = get(predecessorMap, pathEdgeSource); pathEdgeSource = boost::source(edge, m_Graph); pathEdgeTarget = boost::target(edge, m_Graph); TubeDescriptorType tube(pathEdgeSource, pathEdgeTarget); MITK_INFO << "tube [" << tube.first << "," << tube.second << "]"; solutionPath.push_back(tube); } while (pathEdgeSource != startTube.second); return solutionPath; } std::vector mitk::TubeGraph::SearchPathToPeriphery( const mitk::TubeGraph::TubeDescriptorType &startTube /*, std::vector barrier*/) { std::vector pathToPeriphery; if (m_RootTube == ErrorId) { m_RootTube = this->GetThickestTube(); if (m_Root == m_RootTube.first) m_Root = m_RootTube.second; else m_Root = m_RootTube.first; } // Attention!! No check which one is the right one DirectedGraphType directedGraph = this->GetDirectedGraph(m_Root); // Only the target vertex: it's a directed Graph, and we want only the "after tube" tubes and the clicked ones // this->GetOutEdgesOfAVertex(startTube.first, directedGraph, pathToPeriphery); pathToPeriphery.push_back(startTube); this->GetOutEdgesOfAVertex(startTube.second, directedGraph, pathToPeriphery); return pathToPeriphery; } void mitk::TubeGraph::GetOutEdgesOfAVertex(mitk::TubeGraph::VertexDescriptorType vertex, mitk::TubeGraph::DirectedGraphType &directedGraph, std::vector &pathToPeriphery) { typedef boost::graph_traits::out_edge_iterator OutEdgeIteratorType; std::pair outEdges = boost::out_edges(vertex, directedGraph); for (; outEdges.first != outEdges.second; ++outEdges.first) { TubeDescriptorType tube; tube.first = boost::source(*outEdges.first, directedGraph); tube.second = boost::target(*outEdges.first, directedGraph); if (std::find(pathToPeriphery.begin(), pathToPeriphery.end(), tube) == pathToPeriphery.end()) { pathToPeriphery.push_back(tube); this->GetOutEdgesOfAVertex(tube.second, directedGraph, pathToPeriphery); } } } mitk::TubeGraph::TubeDescriptorType mitk::TubeGraph::GetThickestTube() { TubeDescriptorType thickestTube; float tubeDiameter = 0.0; EdgeIteratorType iterator, end; boost::tie(iterator, end) = boost::edges(m_Graph); for (; iterator != end; ++iterator) { TubeGraphEdge edge = this->GetEdge(*iterator); std::pair soureTargetPair = this->GetVerticesOfAnEdge(*iterator); float tempDiameter = edge.GetEdgeAverageDiameter(soureTargetPair.first, soureTargetPair.second); if (tempDiameter > tubeDiameter) { tubeDiameter = tempDiameter; thickestTube.first = this->GetVertexDescriptor(soureTargetPair.first); thickestTube.second = this->GetVertexDescriptor(soureTargetPair.second); } } return thickestTube; } mitk::TubeGraph::DirectedGraphType mitk::TubeGraph::GetDirectedGraph(VertexDescriptorType startVertex) { DirectedGraphType directedGraph(boost::num_vertices(m_Graph)); DirectedGraphBfsVisitor vis(this, directedGraph); boost::breadth_first_search(m_Graph, startVertex, visitor(vis)); return directedGraph; } mitk::TubeGraph::Pointer mitk::TubeGraph::CreateSubGraph(std::vector subGraphTubes) { TubeGraph::Pointer subGraph = new TubeGraph(); // store the descriptor from origin graph to the correspondent new descriptors std::map vertexDescriptorOldToNewMap; // add a new edge and if necessary also the vertices of each tube to the new sub graph for (auto it = subGraphTubes.begin(); it != subGraphTubes.end(); it++) { // search for the source vertex in the subgraph; if it is already added continue, otherwise add it if (vertexDescriptorOldToNewMap.find(it->first) == vertexDescriptorOldToNewMap.end()) { // add the vertex to the subgraph VertexDescriptorType newVertexDescriptor = subGraph->AddVertex(this->GetVertex(it->first)); // add the pair of descriptor from the origin graph to the descriptor from the subgraph vertexDescriptorOldToNewMap.insert( std::pair(it->first, newVertexDescriptor)); } // and now...search for the target vertex... if (vertexDescriptorOldToNewMap.find(it->second) == vertexDescriptorOldToNewMap.end()) { VertexDescriptorType newVertexDescriptor = subGraph->AddVertex(this->GetVertex(it->second)); vertexDescriptorOldToNewMap.insert( std::pair(it->second, newVertexDescriptor)); } // Get the EdgeDescriptor from origin graph EdgeDescriptorType edgeDescriptor = this->GetEdgeDescriptorByVerices(it->first, it->second); TubeGraphEdge oldEdge = this->GetEdge(edgeDescriptor); // AddEdge needs the source vertex, the target vertex and the edge data // source Vertex: get the subgraph VertexDescriptor by the origin descriptor (tubeDescriptor->first)from the // assigning map // target Vertex: get the subgraph VertexDescriptor by the origin descriptor (tubeDescriptor->second)from the // assigning map // edge data: copy the TubeGraphEdge object using the origin edge desciptor and the origin graph VertexDescriptorType sourceVertex = vertexDescriptorOldToNewMap[it->first]; VertexDescriptorType targetVertex = vertexDescriptorOldToNewMap[it->second]; - EdgeDescriptorType newEdgeDescriptor = subGraph->AddEdge(sourceVertex, targetVertex, this->GetEdge(edgeDescriptor)); + subGraph->AddEdge(sourceVertex, targetVertex, this->GetEdge(edgeDescriptor)); } subGraph->CopyInformation(this); mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->Initialize(); subGraph->SetGeometry(geometry); this->Modified(); return subGraph; } void mitk::TubeGraph::RemoveSubGraph(std::vector deletedTubes) { for (auto it = deletedTubes.begin(); it != deletedTubes.end(); it++) { VertexDescriptorType source = it->first; VertexDescriptorType target = it->second; EdgeDescriptorType edge = this->GetEdgeDescriptorByVerices(source, target); this->RemoveEdge(edge); if (this->GetAllEdgesOfAVertex(source).size() == 0) { this->RemoveVertex(source); } if (this->GetAllEdgesOfAVertex(target).size() == 0) { this->RemoveVertex(target); } } this->Modified(); } void mitk::TubeGraph::SetRootTube(const TubeDescriptorType &root) { if (root != TubeGraph::ErrorId) { m_RootTube = root; if (m_Root == root.first) m_Root = root.second; else m_Root = root.first; this->Modified(); } } void mitk::TubeGraph::SetRoot(const VertexDescriptorType &root) { if (root != TubeGraph::ErrorId.first) { m_Root = root; } } mitk::TubeGraph::TubeDescriptorType mitk::TubeGraph::GetRootTube() { return m_RootTube; } mitk::TubeGraph::VertexDescriptorType mitk::TubeGraph::GetRootVertex() { return m_Root; } mitk::TubeGraph &mitk::TubeGraph::operator=(const mitk::TubeGraph &rhs) { UndirectedGraph::operator=(rhs); return *this; } diff --git a/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp b/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp index d8134317b1..61d35234fc 100644 --- a/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp +++ b/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp @@ -1,609 +1,597 @@ /*=================================================================== 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 "mitkTubeGraphIO.h" #include "mitkCircularProfileTubeElement.h" #include "mitkTubeGraphDefinitions.h" #include "mitkTubeGraphProperty.h" #include #include #include #include namespace mitk { TubeGraphIO::TubeGraphIO(const TubeGraphIO &other) : AbstractFileIO(other) {} TubeGraphIO::TubeGraphIO() : AbstractFileIO( mitk::TubeGraph::GetStaticNameOfClass(), mitk::TubeGraphIO::TUBEGRAPH_MIMETYPE(), "Tube Graph Structure File") { this->RegisterService(); } std::vector TubeGraphIO::Read() { std::locale::global(std::locale("C")); std::vector> result; InputStream stream(this); TiXmlDocument doc; stream >> doc; if (!doc.Error()) { TubeGraph::Pointer newTubeGraph = TubeGraph::New(); TiXmlHandle hDoc(&doc); TiXmlElement *pElem; TiXmlHandle hRoot(0); pElem = hDoc.FirstChildElement().Element(); // save this for later hRoot = TiXmlHandle(pElem); pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_GEOMETRY).Element(); // read geometry mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->Initialize(); // read origin mitk::Point3D origin; double temp = 0; pElem->Attribute(mitk::TubeGraphDefinitions::XML_ORIGIN_X, &temp); origin[0] = temp; pElem->Attribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Y, &temp); origin[1] = temp; pElem->Attribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Z, &temp); origin[2] = temp; geometry->SetOrigin(origin); // read spacing Vector3D spacing; pElem->Attribute(mitk::TubeGraphDefinitions::XML_SPACING_X, &temp); spacing.SetElement(0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_SPACING_Y, &temp); spacing.SetElement(1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_SPACING_Z, &temp); spacing.SetElement(2, temp); geometry->SetSpacing(spacing); // read transform vtkMatrix4x4 *m = vtkMatrix4x4::New(); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_XX, &temp); m->SetElement(0, 0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_XY, &temp); m->SetElement(1, 0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_XZ, &temp); m->SetElement(2, 0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_YX, &temp); m->SetElement(0, 1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_YY, &temp); m->SetElement(1, 1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_YZ, &temp); m->SetElement(2, 1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZX, &temp); m->SetElement(0, 2, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZY, &temp); m->SetElement(1, 2, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZZ, &temp); m->SetElement(2, 2, temp); m->SetElement(0, 3, origin[0]); m->SetElement(1, 3, origin[1]); m->SetElement(2, 3, origin[2]); m->SetElement(3, 3, 1); geometry->SetIndexToWorldTransformByVtkMatrix(m); geometry->SetImageGeometry(false); // read tube graph // read vertices pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_VERTICES).Element(); if (pElem != 0) { // walk through the vertices - TiXmlElement *vertexElement = pElem->FirstChildElement(); - - for (vertexElement; vertexElement; vertexElement = vertexElement->NextSiblingElement()) + for (TiXmlElement *vertexElement = pElem->FirstChildElement(); vertexElement != nullptr; vertexElement = vertexElement->NextSiblingElement()) { int vertexID(0); mitk::Point3D coordinate; coordinate.Fill(0.0); double diameter(0); vertexElement->Attribute(mitk::TubeGraphDefinitions::XML_VERTEX_ID, &vertexID); TiXmlElement *tubeElement = vertexElement->FirstChildElement(); tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, &temp); coordinate[0] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, &temp); coordinate[1] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, &temp); coordinate[2] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, &diameter); mitk::TubeGraphVertex vertexData; mitk::CircularProfileTubeElement *newElement = new mitk::CircularProfileTubeElement(coordinate, diameter); vertexData.SetTubeElement(newElement); mitk::TubeGraph::VertexDescriptorType newVertex = newTubeGraph->AddVertex(vertexData); - if (newVertex != vertexID) + if (static_cast(newVertex) != vertexID) { MITK_ERROR << "Aborting tube graph creation, different vertex ids."; return result; ; } } } // read edges pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_EDGES).Element(); - if (pElem != 0) + if (pElem != nullptr) { // walk through the edges - TiXmlElement *edgeElement = pElem->FirstChildElement(); - - for (edgeElement; edgeElement; edgeElement = edgeElement->NextSiblingElement()) + auto edgeElement = pElem->FirstChildElement(); + for ( ; edgeElement != nullptr; edgeElement = edgeElement->NextSiblingElement()) { int edgeID(0), edgeSourceID(0), edgeTargetID(0); mitk::Point3D coordinate; double diameter(0); edgeElement->Attribute(mitk::TubeGraphDefinitions::XML_EDGE_ID, &edgeID); edgeElement->Attribute(mitk::TubeGraphDefinitions::XML_EDGE_SOURCE_ID, &edgeSourceID); edgeElement->Attribute(mitk::TubeGraphDefinitions::XML_EDGE_TARGET_ID, &edgeTargetID); mitk::TubeGraphEdge edgeData; - TiXmlElement *tubeElement = edgeElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_ELEMENT); - for (tubeElement; tubeElement; tubeElement = tubeElement->NextSiblingElement()) + for (TiXmlElement *tubeElement = edgeElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_ELEMENT); tubeElement != nullptr; tubeElement = tubeElement->NextSiblingElement()) { tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, &temp); coordinate[0] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, &temp); coordinate[1] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, &temp); coordinate[2] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, &diameter); mitk::CircularProfileTubeElement *newElement = new mitk::CircularProfileTubeElement(coordinate, diameter); edgeData.AddTubeElement(newElement); } try { newTubeGraph->AddEdge(edgeSourceID, edgeTargetID, edgeData); } catch (const std::runtime_error &error) { MITK_ERROR << error.what(); return result; } } } // Compute bounding box BoundingBox::Pointer bb = this->ComputeBoundingBox(newTubeGraph); geometry->SetBounds(bb->GetBounds()); MITK_INFO << "Tube Graph read"; MITK_INFO << "Edge numb:" << newTubeGraph->GetNumberOfEdges() << " Vertices: " << newTubeGraph->GetNumberOfVertices(); MITK_INFO << "Reading tube graph property"; mitk::TubeGraphProperty::Pointer newProperty = mitk::TubeGraphProperty::New(); // read label groups pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_LABELGROUPS).Element(); if (pElem != 0) { // walk through the label groups - TiXmlElement *labelGroupElement = pElem->FirstChildElement(); - - for (labelGroupElement; labelGroupElement; labelGroupElement = labelGroupElement->NextSiblingElement()) + for (TiXmlElement *labelGroupElement = pElem->FirstChildElement(); labelGroupElement != nullptr; labelGroupElement = labelGroupElement->NextSiblingElement()) { mitk::TubeGraphProperty::LabelGroup *newLabelGroup = new mitk::TubeGraphProperty::LabelGroup(); const char *labelGroupName; labelGroupName = labelGroupElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str()); if (labelGroupName) newLabelGroup->labelGroupName = labelGroupName; - // labelGroupElement->Attribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME, &labelGroupName); - // newLabelGroup.labeGroupName = labelGroupName; - - TiXmlElement *labelElement = labelGroupElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_LABEL); - for (labelElement; labelElement; labelElement = labelElement->NextSiblingElement()) + for (TiXmlElement *labelElement = labelGroupElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_LABEL); labelElement != nullptr; labelElement = labelElement->NextSiblingElement()) { mitk::TubeGraphProperty::LabelGroup::Label *newLabel = new mitk::TubeGraphProperty::LabelGroup::Label(); const char *labelName; bool isVisible = true; Color color; labelName = labelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str()); if (labelName) newLabel->labelName = labelName; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_VISIBILITY, &temp); if (temp == 0) isVisible = false; else isVisible = true; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_R, &temp); color[0] = temp; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_G, &temp); color[1] = temp; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_B, &temp); color[2] = temp; newLabel->isVisible = isVisible; newLabel->labelColor = color; newLabelGroup->labels.push_back(newLabel); } newProperty->AddLabelGroup(newLabelGroup, newProperty->GetLabelGroups().size()); } } // read attributations pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTIONS).Element(); if (pElem != 0) { - TiXmlElement *tubeToLabelElement = pElem->FirstChildElement(); std::map tubeToLabelsMap; - for (tubeToLabelElement; tubeToLabelElement; tubeToLabelElement = tubeToLabelElement->NextSiblingElement()) + for (TiXmlElement *tubeToLabelElement = pElem->FirstChildElement(); tubeToLabelElement != nullptr; tubeToLabelElement = tubeToLabelElement->NextSiblingElement()) { TubeGraph::TubeDescriptorType tube; mitk::TubeGraphProperty::LabelGroup *labelGroup = new mitk::TubeGraphProperty::LabelGroup(); mitk::TubeGraphProperty::LabelGroup::Label *label = new mitk::TubeGraphProperty::LabelGroup::Label(); tubeToLabelElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, &temp); tube.first = temp; tubeToLabelElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, &temp); tube.second = temp; const char *labelGroupName = tubeToLabelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str()); if (labelGroupName) labelGroup = newProperty->GetLabelGroupByName(labelGroupName); const char *labelName = tubeToLabelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str()); if (labelName) label = newProperty->GetLabelByName(labelGroup, labelName); if (tube != TubeGraph::ErrorId && labelGroup != nullptr && label != nullptr) { TubeGraphProperty::TubeToLabelGroupType tubeToLabelGroup(tube, labelGroupName); tubeToLabelsMap.insert( std::pair(tubeToLabelGroup, labelName)); } } if (tubeToLabelsMap.size() > 0) newProperty->SetTubesToLabels(tubeToLabelsMap); } // read annotations pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_ANNOTATIONS).Element(); if (pElem != 0) { - TiXmlElement *annotationElement = pElem->FirstChildElement(); - for (annotationElement; annotationElement; annotationElement = annotationElement->NextSiblingElement()) + for (TiXmlElement *annotationElement = pElem->FirstChildElement(); annotationElement != nullptr; annotationElement = annotationElement->NextSiblingElement()) { mitk::TubeGraphProperty::Annotation *annotation = new mitk::TubeGraphProperty::Annotation(); std::string annotationName; std::string annotationDescription; TubeGraph::TubeDescriptorType tube; annotationName = annotationElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_ANNOTATION_NAME.c_str()); annotation->name = annotationName; annotationDescription = annotationElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_ANNOTATION_DESCRIPTION.c_str()); annotation->description = annotationDescription; annotationElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, &temp); tube.first = temp; annotationElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, &temp); tube.second = temp; if (tube != TubeGraph::ErrorId) { annotation->tube = tube; newProperty->AddAnnotation(annotation); } } } MITK_INFO << "Tube Graph Property read"; newTubeGraph->SetGeometry(geometry); newTubeGraph->SetProperty("Tube Graph.Visualization Information", newProperty); result.push_back(newTubeGraph.GetPointer()); } else { mitkThrow() << "Parsing error at line " << doc.ErrorRow() << ", col " << doc.ErrorCol() << ": " << doc.ErrorDesc(); } return result; } AbstractFileIO::ConfidenceLevel TubeGraphIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; return Supported; } void TubeGraphIO::Write() { OutputStream out(this); if (!out.good()) { mitkThrow() << "Stream not good."; } std::locale previousLocale(out.getloc()); std::locale I("C"); out.imbue(I); const mitk::TubeGraph *tubeGraph = dynamic_cast(this->GetInput()); // Get geometry of the tube graph mitk::Geometry3D::Pointer geometry = dynamic_cast(tubeGraph->GetGeometry()); // Get property of the tube graph mitk::TubeGraphProperty::Pointer tubeGraphProperty = dynamic_cast( tubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer()); // Create XML document TiXmlDocument documentXML; { // Begin document TiXmlDeclaration *declXML = new TiXmlDeclaration("1.0", "", ""); documentXML.LinkEndChild(declXML); TiXmlElement *mainXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_TUBEGRAPH_FILE); mainXML->SetAttribute(mitk::TubeGraphDefinitions::XML_FILE_VERSION, mitk::TubeGraphDefinitions::VERSION_STRING); documentXML.LinkEndChild(mainXML); TiXmlElement *geometryXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_GEOMETRY); { // begin geometry geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XX, geometry->GetMatrixColumn(0)[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XY, geometry->GetMatrixColumn(0)[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XZ, geometry->GetMatrixColumn(0)[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YX, geometry->GetMatrixColumn(1)[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YY, geometry->GetMatrixColumn(1)[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YZ, geometry->GetMatrixColumn(1)[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZX, geometry->GetMatrixColumn(2)[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZY, geometry->GetMatrixColumn(2)[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZZ, geometry->GetMatrixColumn(2)[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_X, geometry->GetOrigin()[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Y, geometry->GetOrigin()[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Z, geometry->GetOrigin()[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_SPACING_X, geometry->GetSpacing()[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Y, geometry->GetSpacing()[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Z, geometry->GetSpacing()[2]); } // end geometry mainXML->LinkEndChild(geometryXML); TiXmlElement *verticesXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_VERTICES); { // begin vertices section std::vector vertexVector = tubeGraph->GetVectorOfAllVertices(); for (unsigned int index = 0; index < vertexVector.size(); index++) { TiXmlElement *vertexXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_VERTEX); vertexXML->SetAttribute(mitk::TubeGraphDefinitions::XML_VERTEX_ID, tubeGraph->GetVertexDescriptor(vertexVector[index])); // element of each vertex const mitk::TubeElement *element = vertexVector[index].GetTubeElement(); TiXmlElement *elementXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ELEMENT); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, element->GetCoordinates().GetElement(0)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, element->GetCoordinates().GetElement(1)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, element->GetCoordinates().GetElement(2)); if (dynamic_cast(element)) elementXML->SetDoubleAttribute( mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, (dynamic_cast(element))->GetDiameter()); else elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, 2); vertexXML->LinkEndChild(elementXML); verticesXML->LinkEndChild(vertexXML); } } // end vertices section mainXML->LinkEndChild(verticesXML); TiXmlElement *edgesXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_EDGES); { // begin edges section std::vector edgeVector = tubeGraph->GetVectorOfAllEdges(); for (unsigned int index = 0; index < edgeVector.size(); index++) { TiXmlElement *edgeXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_EDGE); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_ID, index); std::pair soureTargetPair = tubeGraph->GetVerticesOfAnEdge(tubeGraph->GetEdgeDescriptor(edgeVector[index])); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_SOURCE_ID, tubeGraph->GetVertexDescriptor(soureTargetPair.first)); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_TARGET_ID, tubeGraph->GetVertexDescriptor(soureTargetPair.second)); // begin elements of the edge std::vector elementVector = edgeVector[index].GetElementVector(); for (unsigned int elementIndex = 0; elementIndex < elementVector.size(); elementIndex++) { TiXmlElement *elementXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ELEMENT); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, elementVector[elementIndex]->GetCoordinates().GetElement(0)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, elementVector[elementIndex]->GetCoordinates().GetElement(1)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, elementVector[elementIndex]->GetCoordinates().GetElement(2)); if (dynamic_cast(elementVector[elementIndex])) elementXML->SetDoubleAttribute( mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, (dynamic_cast(elementVector[elementIndex]))->GetDiameter()); else elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, 2); edgeXML->LinkEndChild(elementXML); // elementsXML->LinkEndChild(elementXML); } edgesXML->LinkEndChild(edgeXML); } } // end edges section mainXML->LinkEndChild(edgesXML); TiXmlElement *labelGroupsXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_LABELGROUPS); { // begin label group section std::vector labelGroupVector = tubeGraphProperty->GetLabelGroups(); for (unsigned int index = 0; index < labelGroupVector.size(); index++) { TiXmlElement *labelGroupXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_LABELGROUP); labelGroupXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME, labelGroupVector[index]->labelGroupName); // begin labels of the label group std::vector labelVector = labelGroupVector[index]->labels; for (unsigned int labelIndex = 0; labelIndex < labelVector.size(); labelIndex++) { TiXmlElement *labelXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_LABEL); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_NAME, labelVector[labelIndex]->labelName); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_VISIBILITY, labelVector[labelIndex]->isVisible); labelXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_R, labelVector[labelIndex]->labelColor[0]); labelXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_G, labelVector[labelIndex]->labelColor[1]); labelXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_B, labelVector[labelIndex]->labelColor[2]); labelGroupXML->LinkEndChild(labelXML); } labelGroupsXML->LinkEndChild(labelGroupXML); } } // end labe group section mainXML->LinkEndChild(labelGroupsXML); TiXmlElement *attributionsXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTIONS); { // begin attributions section std::map tubeToLabelGroup = tubeGraphProperty->GetTubesToLabels(); for (std::map::iterator it = tubeToLabelGroup.begin(); it != tubeToLabelGroup.end(); it++) { TiXmlElement *attributXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTION); attributXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, it->first.first.first); attributXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, it->first.first.second); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME, it->first.second); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_NAME, it->second); attributionsXML->LinkEndChild(attributXML); } } // end attributions section mainXML->LinkEndChild(attributionsXML); TiXmlElement *annotationsXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ANNOTATIONS); { // begin annotations section std::vector annotations = tubeGraphProperty->GetAnnotations(); for (unsigned int index = 0; index < annotations.size(); index++) { TiXmlElement *annotationXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ANNOTATION); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_NAME, annotations[index]->name); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_DESCRIPTION, annotations[index]->description); annotationXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, annotations[index]->tube.first); annotationXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, annotations[index]->tube.second); annotationsXML->LinkEndChild(annotationXML); } } // end annotations section mainXML->LinkEndChild(annotationsXML); } // end document TiXmlPrinter printer; printer.SetStreamPrinting(); documentXML.Accept(&printer); out << printer.Str(); } AbstractFileIO::ConfidenceLevel TubeGraphIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; return Supported; } TubeGraphIO *TubeGraphIO::IOClone() const { return new TubeGraphIO(*this); } } const mitk::BoundingBox::Pointer mitk::TubeGraphIO::ComputeBoundingBox(mitk::TubeGraph::Pointer graph) const { BoundingBox::Pointer boundingBox = BoundingBox::New(); BoundingBox::PointIdentifier pointid = 0; BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); ScalarType nullpoint[] = {0, 0, 0}; BoundingBox::PointType p(nullpoint); // traverse the tree and add each point to the pointscontainer mitk::Point3D pos; std::vector vertexVector = graph->GetVectorOfAllVertices(); for (std::vector::iterator vertex = vertexVector.begin(); vertex != vertexVector.end(); ++vertex) { pos = vertex->GetTubeElement()->GetCoordinates(); p[0] = pos[0]; p[1] = pos[1]; p[2] = pos[2]; pointscontainer->InsertElement(pointid++, p); } std::vector edgeVector = graph->GetVectorOfAllEdges(); for (std::vector::iterator edge = edgeVector.begin(); edge != edgeVector.end(); ++edge) { std::vector allElements = edge->GetElementVector(); for (unsigned int index = 0; index < edge->GetNumberOfElements(); index++) { pos = allElements[index]->GetCoordinates(); p[0] = pos[0]; p[1] = pos[1]; p[2] = pos[2]; pointscontainer->InsertElement(pointid++, p); } } boundingBox->SetPoints(pointscontainer); boundingBox->ComputeBoundingBox(); return boundingBox; } diff --git a/Modules/TubeGraph/src/Interactions/mitkTubeGraphPicker.cpp b/Modules/TubeGraph/src/Interactions/mitkTubeGraphPicker.cpp index 4584deb055..b41c8e6332 100644 --- a/Modules/TubeGraph/src/Interactions/mitkTubeGraphPicker.cpp +++ b/Modules/TubeGraph/src/Interactions/mitkTubeGraphPicker.cpp @@ -1,92 +1,92 @@ /*=================================================================== 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 "mitkTubeGraphPicker.h" mitk::TubeGraphPicker::TubeGraphPicker() { m_WorldPosition.Fill(0.0); } mitk::TubeGraphPicker::~TubeGraphPicker() { } void mitk::TubeGraphPicker::SetTubeGraph(const mitk::TubeGraph *tubeGraph) { m_TubeGraph = const_cast(tubeGraph); m_TubeGraphProperty = dynamic_cast(m_TubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer()); } /** * Implements the picking process */ std::pair mitk::TubeGraphPicker::GetPickedTube( const Point3D pickedPosition) { if (!m_TubeGraph) { MITK_ERROR << "mitk::TubeGraphPicker: No tube graph available. Please set an input!" << std::endl; mitk::TubeElement *nullPointer = nullptr; return std::pair(TubeGraph::ErrorId, nullPointer); } m_WorldPosition = pickedPosition; Point3D currentPosition; ScalarType closestDistance = itk::NumericTraits::max(); ScalarType currentDistance = itk::NumericTraits::max(); float currentRadius = 0; TubeGraph::TubeDescriptorType currentTubeId(TubeGraph::ErrorId); TubeGraph::TubeDescriptorType tubeId(TubeGraph::ErrorId); TubeElement *tubeElement; // iterate over all edges and find the edge, which element is near by the clicked point std::vector allEdges = m_TubeGraph->GetVectorOfAllEdges(); for (auto edge = allEdges.begin(); edge != allEdges.end(); ++edge) { std::pair soureTargetPair = m_TubeGraph->GetVerticesOfAnEdge(m_TubeGraph->GetEdgeDescriptor(*edge)); currentTubeId = TubeGraph::TubeDescriptorType(m_TubeGraph->GetVertexDescriptor(soureTargetPair.first), m_TubeGraph->GetVertexDescriptor(soureTargetPair.second)); // check if the tube is visible, if not pass this tube. User can not choose a tube, which he can't see if (m_TubeGraphProperty->IsTubeVisible(currentTubeId)) { std::vector allElements = edge->GetElementVector(); for (unsigned int index = 0; index < edge->GetNumberOfElements(); index++) { currentPosition = allElements[index]->GetCoordinates(); if (dynamic_cast(allElements[index])) currentRadius = ((dynamic_cast(allElements[index]))->GetDiameter()) / 2; else currentRadius = 0; // calculate point->point distance currentDistance = m_WorldPosition.EuclideanDistanceTo(currentPosition); if (currentDistance < closestDistance && (currentDistance - currentRadius) < 1.0) { closestDistance = currentDistance; tubeId = currentTubeId; tubeElement = allElements[index]; } } } } - std::pair pickedTubeWithElement(tubeId, tubeElement); - return pickedTubeWithElement; + + return std::make_pair(tubeId, tubeElement); } diff --git a/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp b/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp index e6cebb4712..fc7063afef 100644 --- a/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp +++ b/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp @@ -1,450 +1,432 @@ /*=================================================================== 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 "mitkTubeGraphProperty.h" #include mitk::TubeGraphProperty::TubeGraphProperty() /*: m_LabelGroups(), m_ActiveTubes(), m_TubeToLabelsMap()*/ { } mitk::TubeGraphProperty::TubeGraphProperty(const mitk::TubeGraphProperty &other) : BaseProperty(other), m_ActiveTubes(other.m_ActiveTubes), m_LabelGroups(other.m_LabelGroups), m_TubeToLabelsMap(other.m_TubeToLabelsMap), m_Annotations(other.m_Annotations) { } mitk::TubeGraphProperty::~TubeGraphProperty() { m_ActiveTubes.clear(); m_TubeToLabelsMap.clear(); for (auto it = m_LabelGroups.begin(); it != m_LabelGroups.end(); it++) delete *it; m_LabelGroups.clear(); } bool mitk::TubeGraphProperty::IsTubeVisible(const TubeDescriptorType &tube) { // search for any label settings for the tube if (m_LabelGroups.size() > 0) { for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it).first.first)) { // At the moment only the first entry is considered LabelGroup *lg = this->GetLabelGroupByName((*it).first.second); LabelGroup::Label *label = this->GetLabelByName(lg, (*it).second); return label->isVisible; } } // If nothing is found, look if the first labelgroup is visible for "undefined" label LabelGroup::Label *label = this->GetLabelByName((*m_LabelGroups.begin()), "Undefined"); return label->isVisible; } else return true; } void mitk::TubeGraphProperty::SetTubeActive(const TubeDescriptorType &tube, const bool &active) { // set active if (active) { for (std::vector::const_iterator it = m_ActiveTubes.begin(); it != m_ActiveTubes.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it))) { return; } } // if not found, add it m_ActiveTubes.push_back(tube); this->Modified(); } // set deactive else { for (auto it = m_ActiveTubes.begin(); it != m_ActiveTubes.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it))) { // if found, delete it m_ActiveTubes.erase(it); this->Modified(); return; } } } //// render new activation // RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::TubeGraphProperty::SetTubesActive(std::vector &tubes) { for (auto it = tubes.begin(); it != tubes.end(); it++) this->SetTubeActive(*it, true); } bool mitk::TubeGraphProperty::IsTubeActive(const TubeDescriptorType &tube) { for (std::vector::const_iterator it = m_ActiveTubes.begin(); it != m_ActiveTubes.end(); it++) { if (this->TubeDescriptorsCompare((*it), tube)) return true; } return false; } std::vector mitk::TubeGraphProperty::GetActiveTubes() { return m_ActiveTubes; } void mitk::TubeGraphProperty::DeactivateAllTubes() { // if (!m_ActiveTubes.empty()) m_ActiveTubes.clear(); this->Modified(); } void mitk::TubeGraphProperty::AddAnnotation(mitk::TubeGraphProperty::Annotation *annotation) { for (auto it = m_Annotations.begin(); it != m_Annotations.end(); it++) { if ((*it)->name == annotation->name) { MITK_INFO << "An Annotation with this name already exists."; return; } } m_Annotations.push_back(annotation); } mitk::TubeGraphProperty::Annotation *mitk::TubeGraphProperty::GetAnnotationByName(std::string annotation) { for (auto it = m_Annotations.begin(); it != m_Annotations.end(); it++) { if ((*it)->name == annotation) return *it; } return nullptr; } std::vector mitk::TubeGraphProperty::GetAnnotations() { return m_Annotations; } void mitk::TubeGraphProperty::RemoveAnnotation(mitk::TubeGraphProperty::Annotation *annotation) { for (auto it = m_Annotations.begin(); it != m_Annotations.end(); it++) { if ((*it)->name == annotation->name) { m_Annotations.erase(it); return; } } } mitk::Color mitk::TubeGraphProperty::GetColorOfTube(const TubeDescriptorType &tube) { Color color; // grey as default color color[0] = 150; color[1] = 150; color[2] = 150; bool isActive(false); bool hasLabel(false); // search the vector with active tubes first. If the tube is active it can not have another color. for (std::vector::const_iterator it = m_ActiveTubes.begin(); it != m_ActiveTubes.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it))) { color[0] = 255; color[1] = 255; color[2] = 0; isActive = true; break; } } if (!isActive) { // So let see which label is activ on this tube and which color is set. if (m_LabelGroups.size() > 0) { for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it).first.first)) { // At the moment only the first entry is considered LabelGroup *lg = this->GetLabelGroupByName((*it).first.second); LabelGroup::Label *label = this->GetLabelByName(lg, (*it).second); color = label->labelColor; hasLabel = true; } } // If nothing is found, look if the first labelgroup is visible for "undefined" label if (!hasLabel) { LabelGroup::Label *label = this->GetLabelByName(*(m_LabelGroups.begin()), "Undefined"); color = label->labelColor; } } } return color; } void mitk::TubeGraphProperty::SetLabelForActivatedTubes(LabelGroup *labelGroup, LabelGroup::Label *label) { bool isUndefined(label->labelName.compare("Undefined") == 0); for (unsigned int i = 0; i < m_ActiveTubes.size(); i++) { bool isInList(false); for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { if ((this->TubeDescriptorsCompare(it->first.first, m_ActiveTubes[i])) && (labelGroup->labelGroupName.compare(it->first.second) == 0)) { // If tube has a label fromlabel group, deleted if "undefined" is clicked. if (isUndefined) { m_TubeToLabelsMap.erase(it); break; } else { it->second = label->labelName; isInList = true; } } } if (!isInList && !isUndefined) { TubeToLabelGroupType tubeToLabelGroup(m_ActiveTubes[i], labelGroup->labelGroupName); m_TubeToLabelsMap.insert(std::pair(tubeToLabelGroup, label->labelName)); } } this->Modified(); m_ActiveTubes.clear(); } void mitk::TubeGraphProperty::SetTubesToLabels(std::map tubeToLabelMap) { m_TubeToLabelsMap = tubeToLabelMap; } std::map mitk::TubeGraphProperty::GetTubesToLabels() { return m_TubeToLabelsMap; } void mitk::TubeGraphProperty::AddLabelGroup(LabelGroup *labelGroup, unsigned int position) { // Check if the name is unique for (auto it = m_LabelGroups.begin(); it != m_LabelGroups.end(); it++) { if (labelGroup->labelGroupName.compare((*it)->labelGroupName) == 0) { MITK_ERROR << "The label group must be unique! This name already exists!"; return; } } // Add the label group at position, if you can, otherwise put it at the end of the vector if (position < m_LabelGroups.size() - 1) { auto it = m_LabelGroups.begin() + position; m_LabelGroups.insert(it, labelGroup); } else { m_LabelGroups.push_back(labelGroup); // when given position is larger then vector size, the label group will be added at the last position if (position >= m_LabelGroups.size()) MITK_INFO << "Position is out of space. So the label group was added on last position: " << m_LabelGroups.size() - 1; } } void mitk::TubeGraphProperty::RemoveLabelGroup(LabelGroup *labelGroup) { // find labelGroup in vector auto foundElement = std::find(m_LabelGroups.begin(), m_LabelGroups.end(), labelGroup); unsigned int pos = foundElement - m_LabelGroups.begin(); // found it? delete it! if (pos < m_LabelGroups.size()) { // delete every assignment to a tube for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { if (labelGroup->labelGroupName.compare((*it).first.second) == 0) m_TubeToLabelsMap.erase(it); } m_LabelGroups.erase(foundElement); } else { MITK_ERROR << "Could not find the label group!"; return; } } void mitk::TubeGraphProperty::SetLabelColor(LabelGroup::Label *label, mitk::Color color) { // LabelGroup? Check if it is a label in property class if (label) { label->labelColor = color; this->Modified(); } } void mitk::TubeGraphProperty::SetLabelVisibility(LabelGroup::Label *label, bool isVisible) { // LabelGroup? Check if it is a label in property class if (label) { label->isVisible = isVisible; this->Modified(); } } void mitk::TubeGraphProperty::RenameLabel(LabelGroup *labelGroup, LabelGroup::Label *label, std::string newName) { // LabelGroup? Check if it is a label in property class if (label) { // rename the label in the assignement vector for tubes for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { // Label group fit? if (labelGroup->labelGroupName.compare((*it).first.second) == 0) // label fit? if (label->labelName.compare((*it).second) == 0) // rename it (*it).second = newName; } // rename the label in label group label->labelName = newName; } } mitk::TubeGraphProperty::LabelGroupSetType mitk::TubeGraphProperty::GetLabelGroups() { return m_LabelGroups; } unsigned int mitk::TubeGraphProperty::GetNumberOfLabelGroups() { return m_LabelGroups.size(); } unsigned int mitk::TubeGraphProperty::GetIndexOfLabelGroup(mitk::TubeGraphProperty::LabelGroup *labelGroup) { unsigned int pos = std::find(m_LabelGroups.begin(), m_LabelGroups.end(), labelGroup) - m_LabelGroups.begin(); if (pos < m_LabelGroups.size()) return pos; // label group is not in property class else { MITK_ERROR << "Could not find such a label group!"; return m_LabelGroups.size(); } } mitk::TubeGraphProperty::LabelGroup *mitk::TubeGraphProperty::GetLabelGroupByName(std::string labelGroup) { for (auto it = m_LabelGroups.begin(); it != m_LabelGroups.end(); it++) { if (labelGroup.compare((*it)->labelGroupName) == 0) return (*it); } MITK_ERROR << "Could not find such a label group!"; return nullptr; } mitk::TubeGraphProperty::LabelGroup::Label *mitk::TubeGraphProperty::GetLabelByName(LabelGroup *labelGroup, std::string labelName) { for (auto it = labelGroup->labels.begin(); it != labelGroup->labels.end(); it++) { if (labelName.compare((*it)->labelName) == 0) return (*it); } MITK_ERROR << "Could not find such a label!"; return nullptr; } bool mitk::TubeGraphProperty::TubeDescriptorsCompare(const TubeDescriptorType &tube1, const TubeDescriptorType &tube2) { if (((tube1.first == tube2.first) && (tube1.second == tube2.second)) || ((tube1.first == tube2.second) && (tube1.second == tube2.first))) return true; else return false; } -bool mitk::TubeGraphProperty::IsEqual(const BaseProperty &property) const +bool mitk::TubeGraphProperty::IsEqual(const BaseProperty &) const { // TODO see ResectionProposalDescriptorProperty - // const TubeGraphProperty& other = - // static_cast(property); - - return true - // same number of elements, same name and corresponding elements are equal (set datatype stores them sorted!) - /*(m_LDs.size() == other.GetLabelDesriptorSet().size()) - && (m_ResectionProposalName == other.GetResectionProposalName()) - && std::equal(m_LDs.begin(), m_LDs.end(), other.GetLabelDesriptorSet().begin())*/; + return true; } -bool mitk::TubeGraphProperty::Assign(const BaseProperty &property) +bool mitk::TubeGraphProperty::Assign(const BaseProperty &) { // TODO see ResectionProposalDescriptorProperty - // const TubeGraphProperty& other = - // static_cast(property); - /* this->m_LDs = other.m_LDs; - this->m_ResectionProposalName = other.m_ResectionProposalName;*/ return true; } std::string mitk::TubeGraphProperty::GetValueAsString() const { // TODO - std::stringstream result; - /* result << "Tube graph: " << this->GetResectionProposalName(); - result << "\n Number of labels: " << this->GetNumberOfLabels() << "\n"; - for (LabelDesriptorSet::const_iterator it = m_LDs.begin(); it != m_LDs.end(); it++) - result << "Label " << it->m_Name<< ": " - << "Label = " << it->m_Label << ", ResectionType = " << it->m_ResectionType - << ", Volume = " << it->m_Volume << "\n";*/ - return result.str(); + return ""; } itk::LightObject::Pointer mitk::TubeGraphProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); return result; } diff --git a/Modules/TubeGraph/src/Rendering/mitkTubeGraphVtkMapper3D.cpp b/Modules/TubeGraph/src/Rendering/mitkTubeGraphVtkMapper3D.cpp index 2dd6f343f3..d6a2358b66 100644 --- a/Modules/TubeGraph/src/Rendering/mitkTubeGraphVtkMapper3D.cpp +++ b/Modules/TubeGraph/src/Rendering/mitkTubeGraphVtkMapper3D.cpp @@ -1,758 +1,757 @@ /*=================================================================== 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 "mitkTubeGraphVtkMapper3D.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include mitk::TubeGraphVtkMapper3D::TubeGraphVtkMapper3D() { } mitk::TubeGraphVtkMapper3D::~TubeGraphVtkMapper3D() { } const mitk::TubeGraph *mitk::TubeGraphVtkMapper3D::GetInput() { - return dynamic_cast(GetData()); + return dynamic_cast(GetDataNode()->GetData()); } vtkProp *mitk::TubeGraphVtkMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) { return m_LSH.GetLocalStorage(renderer)->m_vtkTubeGraphAssembly; } void mitk::TubeGraphVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { bool renderTubeGraph(false); LocalStorage *ls = m_LSH.GetLocalStorage(renderer); TubeGraph::Pointer tubeGraph = const_cast(this->GetInput()); TubeGraphProperty::Pointer tubeGraphProperty = dynamic_cast(tubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer()); if (tubeGraph.IsNull() || tubeGraphProperty.IsNull()) { itkWarningMacro(<< "Input of tube graph mapper is nullptr!"); return; } // Check if the tube graph has changed; if the data has changed, generate the spheres and tubes new; if (tubeGraph->GetMTime() > ls->m_lastGenerateDataTime) { this->GenerateTubeGraphData(renderer); renderTubeGraph = true; } else { // Check if the tube graph property has changed; if the property has changed, render the visualization information // new; if (tubeGraphProperty->GetMTime() > ls->m_lastRenderDataTime) { this->RenderTubeGraphPropertyInformation(renderer); renderTubeGraph = true; } } if (renderTubeGraph) { std::vector alreadyRenderedVertexList; // don't render the sphere which is the root of the graph; so add it to the list before; // TODO check both spheres TubeGraph::VertexDescriptorType root = tubeGraph->GetRootVertex(); alreadyRenderedVertexList.push_back(root); for (std::map>::iterator itTubes = ls->m_vtkTubesActorMap.begin(); itTubes != ls->m_vtkTubesActorMap.end(); itTubes++) { if (tubeGraphProperty->IsTubeVisible(itTubes->first)) { // add tube actor to assembly ls->m_vtkTubeGraphAssembly->AddPart(itTubes->second); // render the clipped spheres as end-cups of a tube and connections between tubes if (std::find(alreadyRenderedVertexList.begin(), alreadyRenderedVertexList.end(), itTubes->first.first) == alreadyRenderedVertexList.end()) { std::map>::iterator itSourceSphere = ls->m_vtkSpheresActorMap.find(itTubes->first.first); if (itSourceSphere != ls->m_vtkSpheresActorMap.end()) ls->m_vtkTubeGraphAssembly->AddPart(itSourceSphere->second); alreadyRenderedVertexList.push_back(itSourceSphere->first); } if (std::find(alreadyRenderedVertexList.begin(), alreadyRenderedVertexList.end(), itTubes->first.second) == alreadyRenderedVertexList.end()) { std::map>::iterator itTargetSphere = ls->m_vtkSpheresActorMap.find(itTubes->first.second); if (itTargetSphere != ls->m_vtkSpheresActorMap.end()) ls->m_vtkTubeGraphAssembly->AddPart(itTargetSphere->second); alreadyRenderedVertexList.push_back(itTargetSphere->first); } } } } //// Opacity TODO //{ // float opacity = 1.0f; // if( this->GetDataNode()->GetOpacity(opacity,renderer) ) // ls->m_vtkTubesActor->GetProperty()->SetOpacity( opacity ); //} } void mitk::TubeGraphVtkMapper3D::RenderTubeGraphPropertyInformation(mitk::BaseRenderer *renderer) { MITK_INFO << "Render tube graph property information!"; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); TubeGraph::Pointer tubeGraph = const_cast(this->GetInput()); TubeGraphProperty::Pointer tubeGraphProperty = dynamic_cast(tubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer()); if (tubeGraphProperty.IsNull()) { MITK_INFO << "No tube graph property!! So no special render information..."; return; } std::vector allVertices = tubeGraph->GetVectorOfAllVertices(); for (std::vector::iterator vertex = allVertices.begin(); vertex != allVertices.end(); ++vertex) { TubeGraph::VertexDescriptorType vertexDesc = tubeGraph->GetVertexDescriptor(*vertex); double sphereColorR = 0; double sphereColorG = 0; double sphereColorB = 0; int numberOfVisibleEdges = 0; std::vector allEdgesOfVertex = tubeGraph->GetAllEdgesOfAVertex(vertexDesc); for (std::vector::iterator edge = allEdgesOfVertex.begin(); edge != allEdgesOfVertex.end(); ++edge) { // get edge descriptor EdgeDescriptorType edgeDesc = tubeGraph->GetEdgeDescriptor(*edge); // get source and target vertex descriptor std::pair soureTargetPair = tubeGraph->GetVerticesOfAnEdge(edgeDesc); TubeGraphVertex source = soureTargetPair.first; TubeGraphVertex target = soureTargetPair.second; // build tube descriptor [sourceId,targetId] TubeGraph::TubeDescriptorType tube; tube.first = tubeGraph->GetVertexDescriptor(source); tube.second = tubeGraph->GetVertexDescriptor(target); if (tubeGraphProperty->IsTubeVisible(tube)) { mitk::Color tubeColor = tubeGraphProperty->GetColorOfTube(tube); vtkSmartPointer scalars = ls->m_vtkTubesActorMap[tube]->GetMapper()->GetInput()->GetPointData()->GetScalars(); double color[3]; scalars->GetTuple(0, color); if (color[0] != tubeColor[0] || color[1] != tubeColor[1] || color[2] != tubeColor[2]) { int numberOfPoints = scalars->GetSize(); vtkSmartPointer colorScalars = vtkSmartPointer::New(); colorScalars->SetName("colorScalars"); colorScalars->SetNumberOfComponents(3); colorScalars->SetNumberOfTuples(numberOfPoints); for (int i = 0; i < numberOfPoints; i++) { scalars->InsertTuple3(i, tubeColor[0], tubeColor[1], tubeColor[2]); } ls->m_vtkTubesActorMap[tube]->GetMapper()->GetInput()->GetPointData()->SetActiveScalars("colorScalars"); } sphereColorR += tubeColor[0]; sphereColorG += tubeColor[1]; sphereColorB += tubeColor[2]; numberOfVisibleEdges++; } } if (numberOfVisibleEdges > 0) { sphereColorR /= 255 * numberOfVisibleEdges; sphereColorG /= 255 * numberOfVisibleEdges; sphereColorB /= 255 * numberOfVisibleEdges; } ls->m_vtkSpheresActorMap[vertexDesc]->GetProperty()->SetColor(sphereColorR, sphereColorG, sphereColorB); } ls->m_lastRenderDataTime.Modified(); } void mitk::TubeGraphVtkMapper3D::GenerateTubeGraphData(mitk::BaseRenderer *renderer) { MITK_INFO << "Render tube graph!"; LocalStorage *ls = m_LSH.GetLocalStorage(renderer); ls->m_vtkTubesActorMap.clear(); ls->m_vtkSpheresActorMap.clear(); TubeGraph::Pointer tubeGraph = const_cast(this->GetInput()); TubeGraphProperty::Pointer tubeGraphProperty = dynamic_cast(tubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer()); if (tubeGraphProperty.IsNull()) MITK_INFO << "No tube graph property!! So no special render information..."; // render all edges as tubular structures using the vtkTubeFilter std::vector allEdges = tubeGraph->GetVectorOfAllEdges(); for (std::vector::iterator edge = allEdges.begin(); edge != allEdges.end(); ++edge) { this->GeneratePolyDataForTube(*edge, tubeGraph, tubeGraphProperty, renderer); } // Generate all vertices as spheres std::vector allVertices = tubeGraph->GetVectorOfAllVertices(); for (std::vector::iterator vertex = allVertices.begin(); vertex != allVertices.end(); ++vertex) { this->GeneratePolyDataForFurcation(*vertex, tubeGraph, renderer); if (this->ClipStructures()) { this->ClipPolyData(*vertex, tubeGraph, tubeGraphProperty, renderer); } ls->m_lastGenerateDataTime.Modified(); } } void mitk::TubeGraphVtkMapper3D::GeneratePolyDataForFurcation(mitk::TubeGraphVertex &vertex, const mitk::TubeGraph::Pointer &graph, mitk::BaseRenderer *renderer) { LocalStorage *ls = this->m_LSH.GetLocalStorage(renderer); mitk::Point3D coordinates; float diameter = 2; coordinates = (vertex.GetTubeElement())->GetCoordinates(); if (dynamic_cast(vertex.GetTubeElement())) { diameter = (dynamic_cast(vertex.GetTubeElement()))->GetDiameter(); } vtkSmartPointer sphereSource = vtkSmartPointer::New(); sphereSource->SetCenter(coordinates[0], coordinates[1], coordinates[2]); sphereSource->SetRadius(diameter / 2.0f); sphereSource->SetThetaResolution(12); sphereSource->SetPhiResolution(12); sphereSource->Update(); // generate a actor with a mapper for the sphere vtkSmartPointer sphereMapper = vtkSmartPointer::New(); vtkSmartPointer sphereActor = vtkSmartPointer::New(); sphereMapper->SetInputConnection(sphereSource->GetOutputPort()); sphereActor->SetMapper(sphereMapper); ls->m_vtkSpheresActorMap.insert(std::make_pair(graph->GetVertexDescriptor(vertex), sphereActor)); } void mitk::TubeGraphVtkMapper3D::GeneratePolyDataForTube(mitk::TubeGraphEdge &edge, const mitk::TubeGraph::Pointer &graph, const mitk::TubeGraphProperty::Pointer &graphProperty, mitk::BaseRenderer *renderer) { LocalStorage *ls = this->m_LSH.GetLocalStorage(renderer); // get edge descriptor EdgeDescriptorType edgeDesc = graph->GetEdgeDescriptor(edge); // get source and target vertex descriptor std::pair soureTargetPair = graph->GetVerticesOfAnEdge(edgeDesc); TubeGraphVertex source = soureTargetPair.first; TubeGraphVertex target = soureTargetPair.second; // build tube descriptor [sourceId,targetId] TubeGraph::TubeDescriptorType tube; tube.first = graph->GetVertexDescriptor(source); tube.second = graph->GetVertexDescriptor(target); Color color; if (graphProperty.IsNotNull()) { color = graphProperty->GetColorOfTube(tube); } else { color[0] = 150; color[1] = 150; color[2] = 150; } // add 2 points for the source and target vertices. unsigned int numberOfPoints = edge.GetNumberOfElements() + 2; // Initialize the required data-structures for building // an appropriate input to the tube filter vtkSmartPointer points = vtkSmartPointer::New(); points->SetNumberOfPoints(numberOfPoints); vtkSmartPointer radii = vtkSmartPointer::New(); radii->SetName("radii"); radii->SetNumberOfComponents(1); vtkSmartPointer lines = vtkSmartPointer::New(); vtkSmartPointer colorScalars = vtkSmartPointer::New(); colorScalars->SetName("colorScalars"); colorScalars->SetNumberOfComponents(3); // resize the data-arrays radii->SetNumberOfTuples(numberOfPoints); colorScalars->SetNumberOfTuples(numberOfPoints); lines->InsertNextCell(numberOfPoints); // Add the positions of the source node, the elements along the edge and // the target node as lines to a cell. This cell is used as input // for a Tube Filter mitk::Point3D coordinates; float diameter = 2; unsigned int id = 0; // Source Node coordinates = (source.GetTubeElement())->GetCoordinates(); if (dynamic_cast(source.GetTubeElement())) { diameter = (dynamic_cast(source.GetTubeElement()))->GetDiameter(); } points->InsertPoint(id, coordinates[0], coordinates[1], coordinates[2]); radii->InsertTuple1(id, diameter / 2.0f); colorScalars->InsertTuple3(id, color[0], color[1], color[2]); lines->InsertCellPoint(id); ++id; // Iterate along the edge std::vector allElements = edge.GetElementVector(); for (unsigned int index = 0; index < edge.GetNumberOfElements(); index++) { coordinates = allElements[index]->GetCoordinates(); if (dynamic_cast(allElements[index])) { diameter = (dynamic_cast(allElements[index]))->GetDiameter(); } points->InsertPoint(id, coordinates[0], coordinates[1], coordinates[2]); radii->InsertTuple1(id, diameter / 2.0f); colorScalars->InsertTuple3(id, color[0], color[1], color[2]); lines->InsertCellPoint(id); ++id; } // Target Node coordinates = (target.GetTubeElement())->GetCoordinates(); if (dynamic_cast(target.GetTubeElement())) { diameter = (dynamic_cast(target.GetTubeElement()))->GetDiameter(); } points->InsertPoint(id, coordinates[0], coordinates[1], coordinates[2]); radii->InsertTuple1(id, diameter / 2.0f); colorScalars->InsertTuple3(id, color[0], color[1], color[2]); lines->InsertCellPoint(id); ++id; // Initialize poly data from the point set and the cell array // (representing topology) vtkSmartPointer polyData = vtkSmartPointer::New(); polyData->SetPoints(points); polyData->SetLines(lines); polyData->GetPointData()->AddArray(radii); polyData->GetPointData()->AddArray(colorScalars); polyData->GetPointData()->SetActiveScalars(radii->GetName()); // Generate a tube for all lines in the polydata object double *range = radii->GetRange(); assert(range[0] != 0.0f && range[1] != 0.0f); vtkSmartPointer tubeFilter = vtkSmartPointer::New(); tubeFilter->SetInputData(polyData); tubeFilter->SetRadius(range[0]); tubeFilter->SetRadiusFactor(range[1] / range[0]); if (range[0] != range[1]) tubeFilter->SetVaryRadiusToVaryRadiusByScalar(); tubeFilter->SetNumberOfSides(9); tubeFilter->SidesShareVerticesOn(); tubeFilter->CappingOff(); tubeFilter->Update(); tubeFilter->GetOutput()->GetPointData()->SetActiveScalars("colorScalars"); // generate a actor with a mapper for the vtkSmartPointer tubeMapper = vtkSmartPointer::New(); vtkSmartPointer tubeActor = vtkSmartPointer::New(); tubeMapper->SetInputConnection(tubeFilter->GetOutputPort()); tubeActor->SetMapper(tubeMapper); tubeActor->GetProperty()->SetColor(color[0], color[1], color[2]); ls->m_vtkTubesActorMap.insert(std::pair>(tube, tubeActor)); } void mitk::TubeGraphVtkMapper3D::ClipPolyData(mitk::TubeGraphVertex &vertex, const mitk::TubeGraph::Pointer &graph, const mitk::TubeGraphProperty::Pointer &graphProperty, mitk::BaseRenderer *renderer) { LocalStorage *ls = this->m_LSH.GetLocalStorage(renderer); mitk::Point3D centerVertex = vertex.GetTubeElement()->GetCoordinates(); float diameter = 2; if (dynamic_cast(vertex.GetTubeElement())) { diameter = (dynamic_cast(vertex.GetTubeElement()))->GetDiameter(); } TubeGraph::VertexDescriptorType vertexDesc = graph->GetVertexDescriptor(vertex); std::map> cylinderForClipping; // generate for all edges/tubes cylinders. With this structure you can clip the sphere and the other tubes, so that no // fragments are shown in the tube. std::vector allEdgesOfVertex = graph->GetAllEdgesOfAVertex(vertexDesc); for (std::vector::iterator edge = allEdgesOfVertex.begin(); edge != allEdgesOfVertex.end(); ++edge) { // get edge descriptor EdgeDescriptorType edgeDesc = graph->GetEdgeDescriptor(*edge); // get source and target vertex descriptor - std::pair soureTargetPair = graph->GetVerticesOfAnEdge(edgeDesc); + auto soureTargetPair = graph->GetVerticesOfAnEdge(edgeDesc); TubeGraphVertex source = soureTargetPair.first; TubeGraphVertex target = soureTargetPair.second; // build tube descriptor [sourceId,targetId] TubeGraph::TubeDescriptorType tube; tube.first = graph->GetVertexDescriptor(source); tube.second = graph->GetVertexDescriptor(target); // get reference point in the tube for the direction mitk::Point3D edgeDirectionPoint; // get reference diameter double cylinderDiameter = diameter; float radius = diameter / 2; // if the vertex is the source vertex of the edge get the first element of elementVector; otherwise get the last // element. if (source == vertex) { // if the edge has no element get the other vertex if ((*edge).GetNumberOfElements() != 0) { double lastDistance = 0, distance = 0; unsigned int index = 0; // Get the first element behind the radius of the sphere for (; index < (*edge).GetNumberOfElements(); index++) { mitk::Vector3D diffVec = (*edge).GetTubeElement(index)->GetCoordinates() - centerVertex; distance = std::sqrt(pow(diffVec[0], 2) + pow(diffVec[1], 2) + pow(diffVec[2], 2)); if (distance > radius) break; lastDistance = distance; } // if the last element is not inside the sphere if (index < (*edge).GetNumberOfElements()) { double withinSphereDiameter = diameter, outsideSphereDiameter = diameter, interpolationValue = 0.5; interpolationValue = (radius - lastDistance) / (distance - lastDistance); // if first element is outside of the sphere use sphere diameter and the element diameter for interpolation if (index == 0) { if (dynamic_cast((*edge).GetTubeElement(0))) outsideSphereDiameter = (dynamic_cast((*edge).GetTubeElement(0)))->GetDiameter(); } else { if (dynamic_cast((*edge).GetTubeElement(index - 1))) withinSphereDiameter = (dynamic_cast((*edge).GetTubeElement(index - 1)))->GetDiameter(); if (dynamic_cast((*edge).GetTubeElement(index))) outsideSphereDiameter = (dynamic_cast((*edge).GetTubeElement(index)))->GetDiameter(); } // interpolate the diameter for clipping cylinderDiameter = (1 - interpolationValue) * withinSphereDiameter + interpolationValue * outsideSphereDiameter; } // Get the reference point, so the direction of the tube can be calculated edgeDirectionPoint = (*edge).GetTubeElement(0)->GetCoordinates(); } else { // Get the reference point, so the direction of the tube can be calculated edgeDirectionPoint = target.GetTubeElement()->GetCoordinates(); } } // if vertex is target of the tube else { // if the edge has no element, get the other vertex if ((*edge).GetNumberOfElements() != 0) { double lastDistance = 0, distance = 0; // Get the first element behind the radius of the sphere; now backwards through the element list - unsigned int index = (*edge).GetNumberOfElements() - 1; - for (; index >= 0; index--) + int index = (*edge).GetNumberOfElements(); + for ( ; index >= 0; --index) { mitk::Vector3D diffVec = (*edge).GetTubeElement(index)->GetCoordinates() - centerVertex; distance = std::sqrt(pow(diffVec[0], 2) + pow(diffVec[1], 2) + pow(diffVec[2], 2)); if (distance > radius) break; lastDistance = distance; } if (index >= 0) { double withinSphereDiameter = diameter, outsideSphereDiameter = diameter, interpolationValue = 0.5; interpolationValue = (radius - lastDistance) / (distance - lastDistance); - if (index == (*edge).GetNumberOfElements() - 1) + if (index == static_cast((*edge).GetNumberOfElements() - 1)) { if (dynamic_cast( (*edge).GetTubeElement((*edge).GetNumberOfElements() - 1))) outsideSphereDiameter = (dynamic_cast( (*edge).GetTubeElement((*edge).GetNumberOfElements() - 1))) ->GetDiameter(); } else { if (dynamic_cast((*edge).GetTubeElement(index + 1))) withinSphereDiameter = (dynamic_cast((*edge).GetTubeElement(index + 1)))->GetDiameter(); if (dynamic_cast((*edge).GetTubeElement(index))) outsideSphereDiameter = (dynamic_cast((*edge).GetTubeElement(index)))->GetDiameter(); } - // interpolate the diameter for clipping + // interpolate the diameter for clipping cylinderDiameter = (1 - interpolationValue) * withinSphereDiameter + interpolationValue * outsideSphereDiameter; } - // Get the reference point, so the direction of the tube can be calculated edgeDirectionPoint = (*edge).GetTubeElement((*edge).GetNumberOfElements() - 1)->GetCoordinates(); } else { // Get the reference point, so the direction of the tube can be calculated edgeDirectionPoint = source.GetTubeElement()->GetCoordinates(); } } //////Calculate the matrix for rotation and translation//// // get the normalized vector for the orientation (tube element direction) mitk::Vector3D vecOrientation; mitk::FillVector3D(vecOrientation, (edgeDirectionPoint[0] - centerVertex[0]), (edgeDirectionPoint[1] - centerVertex[1]), (edgeDirectionPoint[2] - centerVertex[2])); vecOrientation.Normalize(); // generate a random vector mitk::Vector3D vecRandom; mitk::FillVector3D(vecRandom, (rand() % 100 - 50), (rand() % 100 - 50), (rand() % 100 - 50)); // project the random vector on the plane-->orthogonal vector to plane normal; normalize it! mitk::Vector3D vecOrthoToOrientation; vecOrthoToOrientation = vecRandom - (vecRandom * vecOrientation) * vecOrientation; vecOrthoToOrientation.Normalize(); // get the cross product of both orthogonale vectors to get a third one mitk::Vector3D vecCrossProduct; vecCrossProduct = itk::CrossProduct(vecOrientation, vecOrthoToOrientation); vecCrossProduct.Normalize(); // Fill matrix vtkSmartPointer vtkTransformMatrix = vtkSmartPointer::New(); vtkTransformMatrix->Identity(); // 1. column vtkTransformMatrix->SetElement(0, 0, vecOrthoToOrientation[0]); vtkTransformMatrix->SetElement(1, 0, vecOrthoToOrientation[1]); vtkTransformMatrix->SetElement(2, 0, vecOrthoToOrientation[2]); // 2. column vtkTransformMatrix->SetElement(0, 1, vecOrientation[0]); vtkTransformMatrix->SetElement(1, 1, vecOrientation[1]); vtkTransformMatrix->SetElement(2, 1, vecOrientation[2]); // 3. column vtkTransformMatrix->SetElement(0, 2, vecCrossProduct[0]); vtkTransformMatrix->SetElement(1, 2, vecCrossProduct[1]); vtkTransformMatrix->SetElement(2, 2, vecCrossProduct[2]); // 4. column vtkTransformMatrix->SetElement(0, 3, centerVertex[0]); vtkTransformMatrix->SetElement(1, 3, centerVertex[1]); vtkTransformMatrix->SetElement(2, 3, centerVertex[2]); vtkSmartPointer transform = vtkSmartPointer::New(); transform->Concatenate(vtkTransformMatrix); // transform->Translate(centerVertex[0],centerVertex[1],centerVertex[2]); transform->Inverse(); transform->Update(); // Generate plane in center [0,0,0] with n= (0,1,0) as normal vector vtkSmartPointer plane = vtkSmartPointer::New(); plane->SetOrigin(0, 0, 0); plane->SetNormal(0, 1, 0); // Generate a cylinder in center [0,0,0] and the axes of rotation is along the y-axis; radius is vertex diameter/2; vtkSmartPointer cylinder = vtkSmartPointer::New(); cylinder->SetCenter(0, 0, 0); cylinder->SetRadius(cylinderDiameter / 2); // cylinder->SetTransform(transform); // Truncate the infinite cylinder with the plane vtkSmartPointer cutCylinder = vtkSmartPointer::New(); cutCylinder->SetOperationTypeToDifference(); cutCylinder->SetTransform(transform); cutCylinder->AddFunction(cylinder); cutCylinder->AddFunction(plane); cylinderForClipping.insert( std::pair>(tube, cutCylinder)); //// Sample the function // vtkSmartPointer sample = vtkSmartPointer::New(); // sample->SetSampleDimensions(100,100,100); // sample->SetImplicitFunction(cutCylinder); ////double value = 200.0; // double xmin = centerVertex[0]-(2*diameter), xmax = centerVertex[0]+(2*diameter), // ymin = centerVertex[1]-(2*diameter), ymax = centerVertex[1]+(2*diameter), // zmin = centerVertex[2]-(2*diameter), zmax = centerVertex[2]+(2*diameter); // sample->SetModelBounds(xmin, xmax, ymin, ymax, zmin, zmax); // vtkSmartPointer contour =vtkSmartPointer::New(); // contour->SetInputConnection(sample->GetOutputPort()); // contour->SetValue( 0, 0.25); // vtkSmartPointer impMapper = vtkSmartPointer::New(); // impMapper->SetInputConnection (contour->GetOutputPort()); // impMapper->ScalarVisibilityOff(); // vtkSmartPointer impActor = vtkSmartPointer::New(); // impActor->SetMapper(impMapper); // ls->m_vtkTubeGraphAssembly->AddPart(impActor); } double sphereColorR = 0; double sphereColorG = 0; double sphereColorB = 0; for (std::map>::iterator itClipStructure = cylinderForClipping.begin(); itClipStructure != cylinderForClipping.end(); itClipStructure++) { vtkSmartPointer sphereMapper = dynamic_cast(ls->m_vtkSpheresActorMap[vertexDesc]->GetMapper()); if (sphereMapper != nullptr) { // first clip the sphere with the cylinder vtkSmartPointer clipperSphere = vtkSmartPointer::New(); clipperSphere->SetInputData(sphereMapper->GetInput()); clipperSphere->SetClipFunction(itClipStructure->second); clipperSphere->GenerateClippedOutputOn(); clipperSphere->Update(); sphereMapper->SetInputConnection(clipperSphere->GetOutputPort()); sphereMapper->Update(); } mitk::Color tubeColor = graphProperty->GetColorOfTube(itClipStructure->first); sphereColorR += tubeColor[0]; sphereColorG += tubeColor[1]; sphereColorB += tubeColor[2]; // than clip with all other tubes for (std::map>::iterator itTobBeClipped = cylinderForClipping.begin(); itTobBeClipped != cylinderForClipping.end(); itTobBeClipped++) { TubeGraph::TubeDescriptorType toBeClippedTube = itTobBeClipped->first; if (itClipStructure->first != toBeClippedTube) { vtkSmartPointer tubeMapper = dynamic_cast(ls->m_vtkTubesActorMap[toBeClippedTube]->GetMapper()); if (tubeMapper != nullptr) { // first clip the sphere with the cylinder vtkSmartPointer clipperTube = vtkSmartPointer::New(); tubeMapper->Update(); clipperTube->SetInputData(tubeMapper->GetInput()); clipperTube->SetClipFunction(itClipStructure->second); clipperTube->GenerateClippedOutputOn(); clipperTube->Update(); tubeMapper->SetInputConnection(clipperTube->GetOutputPort()); tubeMapper->Update(); } } } } if (cylinderForClipping.size() != 0) { sphereColorR /= 255 * cylinderForClipping.size(); sphereColorG /= 255 * cylinderForClipping.size(); sphereColorB /= 255 * cylinderForClipping.size(); } ls->m_vtkSpheresActorMap[vertexDesc]->GetProperty()->SetColor(sphereColorR, sphereColorG, sphereColorB); } bool mitk::TubeGraphVtkMapper3D::ClipStructures() { DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) { itkWarningMacro(<< "associated node is nullptr!"); return false; } bool clipStructures = false; node->GetBoolProperty("Tube Graph.Clip Structures", clipStructures); return clipStructures; } diff --git a/Modules/XNAT/src/QmitkSelectXnatUploadDestinationDialog.cpp b/Modules/XNAT/src/QmitkSelectXnatUploadDestinationDialog.cpp index 7504900c0f..4573080901 100644 --- a/Modules/XNAT/src/QmitkSelectXnatUploadDestinationDialog.cpp +++ b/Modules/XNAT/src/QmitkSelectXnatUploadDestinationDialog.cpp @@ -1,149 +1,149 @@ #include "QmitkSelectXnatUploadDestinationDialog.h" #include "ui_QmitkSelectXnatUploadDestinationDialog.h" #include #include #include #include #include "QmitkXnatTreeModel.h" #include QmitkSelectXnatUploadDestinationDialog::QmitkSelectXnatUploadDestinationDialog(ctkXnatSession *session, const QStringList &availableResources, QWidget *parent) : QDialog(parent), - ui(new Ui::QmitkSelectXnatUploadDestinationDialog), + m_TreeModel(new QmitkXnatTreeModel), m_Url(""), m_ResourceName(""), - m_CreateNewFolder(false) + m_CreateNewFolder(false), + ui(new Ui::QmitkSelectXnatUploadDestinationDialog) { ui->setupUi(this); - m_TreeModel = new QmitkXnatTreeModel(); m_TreeModel->addDataModel(session->dataModel()); ui->treeView->setModel(m_TreeModel); connect(ui->cbSelectResources, SIGNAL(currentIndexChanged(const QString &)), this, SLOT(OnResourceSelected(const QString &))); connect(ui->treeView, SIGNAL(clicked(const QModelIndex &)), this, SLOT(OnXnatNodeSelected(const QModelIndex &))); connect(ui->rbSelectResource, SIGNAL(toggled(bool)), this, SLOT(OnSelectResource(bool))); connect(ui->rbSelectFromTreeView, SIGNAL(toggled(bool)), this, SLOT(OnSelectFromTreeView(bool))); connect(ui->leResourceName, SIGNAL(textChanged(const QString &)), this, SLOT(OnResourceEntered(const QString &))); connect(ui->pbCancel, SIGNAL(clicked()), this, SLOT(OnCancel())); connect(ui->pbUpload, SIGNAL(clicked()), this, SLOT(OnUpload())); // Initial Setup ui->pbUpload->setEnabled(false); ui->leResourceName->setVisible(false); ui->treeView->hide(); if (availableResources.isEmpty()) { ui->rbSelectFromTreeView->setChecked(true); ui->rbSelectResource->setEnabled(false); } else { foreach (QString resourceName, availableResources) { ui->cbSelectResources->addItem(resourceName); } ui->cbSelectResources->addItem("Create new resource folder..."); } } QmitkSelectXnatUploadDestinationDialog::~QmitkSelectXnatUploadDestinationDialog() { delete ui; } void QmitkSelectXnatUploadDestinationDialog::OnCancel() { this->done(QDialog::Rejected); } void QmitkSelectXnatUploadDestinationDialog::OnUpload() { this->done(QDialog::Accepted); } void QmitkSelectXnatUploadDestinationDialog::OnSelectResource(bool selectResource) { ui->pbUpload->setEnabled(false); ui->cbSelectResources->setVisible(selectResource); ui->leResourceName->setVisible(!selectResource); ui->treeView->setVisible(!selectResource); if (selectResource) ui->cbSelectResources->setCurrentIndex(0); } void QmitkSelectXnatUploadDestinationDialog::OnSelectFromTreeView(bool selectFromTreeView) { ui->pbUpload->setEnabled(false); ui->cbSelectResources->setVisible(!selectFromTreeView); ui->leResourceName->setVisible(!selectFromTreeView); ui->treeView->setVisible(selectFromTreeView); } void QmitkSelectXnatUploadDestinationDialog::SetXnatResourceFolderUrl(const QString &url) { m_Url = url; } ctkXnatObject *QmitkSelectXnatUploadDestinationDialog::GetUploadDestination() { if (ui->rbSelectResource->isChecked() && !m_Url.isEmpty() && !m_ResourceName.isEmpty()) { ctkXnatObject *selectedXnatObject = m_TreeModel->GetXnatObjectFromUrl(m_Url); ctkXnatResource *resource = new ctkXnatResource(); resource->setName(m_ResourceName); resource->setParent(selectedXnatObject); if (!resource->exists()) resource->save(); return resource; } else { QModelIndex index = ui->treeView->selectionModel()->currentIndex(); return m_TreeModel->xnatObject(index); } } void QmitkSelectXnatUploadDestinationDialog::OnResourceSelected(const QString &resource) { if (resource.isEmpty()) ui->pbUpload->setEnabled(false); if (resource != "Create new resource folder...") { ui->pbUpload->setEnabled(true); m_ResourceName = resource; ui->leResourceName->hide(); } else if (resource == "Create new resource folder...") { ui->pbUpload->setEnabled(false); ui->leResourceName->show(); } } void QmitkSelectXnatUploadDestinationDialog::OnResourceEntered(const QString &resourceEntered) { m_CreateNewFolder = !resourceEntered.isEmpty(); ui->pbUpload->setEnabled(m_CreateNewFolder); m_ResourceName = resourceEntered; } void QmitkSelectXnatUploadDestinationDialog::OnXnatNodeSelected(const QModelIndex &index) { if (!index.isValid()) return; ctkXnatObject *selectedObject = m_TreeModel->xnatObject(index); ui->pbUpload->setEnabled(dynamic_cast(selectedObject) != nullptr); } diff --git a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp index 9c7adc87d0..0de34b39ea 100644 --- a/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp +++ b/Plugins/org.mitk.gui.qt.ext/src/QmitkExtWorkbenchWindowAdvisor.cpp @@ -1,1372 +1,1372 @@ /*=================================================================== 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 "QmitkExtWorkbenchWindowAdvisor.h" #include "QmitkExtActionBarAdvisor.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // UGLYYY #include "internal/QmitkExtWorkbenchWindowAdvisorHack.h" #include "internal/QmitkCommonExtPlugin.h" #include "mitkUndoController.h" #include "mitkVerboseLimitedLinearUndo.h" #include #include #include #include QmitkExtWorkbenchWindowAdvisorHack * QmitkExtWorkbenchWindowAdvisorHack::undohack = new QmitkExtWorkbenchWindowAdvisorHack(); QString QmitkExtWorkbenchWindowAdvisor::QT_SETTINGS_FILENAME = "QtSettings.ini"; static bool USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS = false; class PartListenerForTitle: public berry::IPartListener { public: PartListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPartEventTypes() const override { return Events::ACTIVATED | Events::BROUGHT_TO_TOP | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartActivated(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartBroughtToTop(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref.Cast ()) { windowAdvisor->UpdateTitle(false); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& /*ref*/) override { windowAdvisor->UpdateTitle(false); } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (!windowAdvisor->lastActiveEditor.Expired() && ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) { windowAdvisor->UpdateTitle(true); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (!windowAdvisor->lastActiveEditor.Expired() && ref->GetPart(false) == windowAdvisor->lastActiveEditor.Lock()) { windowAdvisor->UpdateTitle(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; class PartListenerForViewNavigator: public berry::IPartListener { public: PartListenerForViewNavigator(QAction* act) : viewNavigatorAction(act) { } Events::Types GetPartEventTypes() const override { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(true); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(false); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(true); } } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.viewnavigatorview") { viewNavigatorAction->setChecked(false); } } private: QAction* viewNavigatorAction; }; class PartListenerForImageNavigator: public berry::IPartListener { public: PartListenerForImageNavigator(QAction* act) : imageNavigatorAction(act) { } Events::Types GetPartEventTypes() const override { return Events::OPENED | Events::CLOSED | Events::HIDDEN | Events::VISIBLE; } void PartOpened(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartClosed(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } void PartVisible(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(true); } } void PartHidden(const berry::IWorkbenchPartReference::Pointer& ref) override { if (ref->GetId()=="org.mitk.views.imagenavigator") { imageNavigatorAction->setChecked(false); } } private: QAction* imageNavigatorAction; }; class PerspectiveListenerForTitle: public berry::IPerspectiveListener { public: PerspectiveListenerForTitle(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa), perspectivesClosed(false) { } Events::Types GetPerspectiveEventTypes() const override { if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED; } else { return Events::ACTIVATED | Events::SAVED_AS | Events::DEACTIVATED | Events::CLOSED | Events::OPENED; } } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveSavedAs(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*oldPerspective*/, const berry::IPerspectiveDescriptor::Pointer& /*newPerspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { windowAdvisor->UpdateTitle(false); } void PerspectiveOpened(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { if (perspectivesClosed) { QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(true); } //GetViewRegistry()->Find("org.mitk.views.imagenavigator"); if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { windowAdvisor->openDicomEditorAction->setEnabled(true); } windowAdvisor->fileSaveProjectAction->setEnabled(true); windowAdvisor->closeProjectAction->setEnabled(true); windowAdvisor->undoAction->setEnabled(true); windowAdvisor->redoAction->setEnabled(true); windowAdvisor->imageNavigatorAction->setEnabled(true); windowAdvisor->viewNavigatorAction->setEnabled(true); windowAdvisor->resetPerspAction->setEnabled(true); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(true); } } perspectivesClosed = false; } void PerspectiveClosed(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& /*perspective*/) override { berry::IWorkbenchWindow::Pointer wnd = windowAdvisor->GetWindowConfigurer()->GetWindow(); bool allClosed = true; if (wnd->GetActivePage()) { QList perspectives(wnd->GetActivePage()->GetOpenPerspectives()); allClosed = perspectives.empty(); } if (allClosed) { perspectivesClosed = true; QListIterator i(windowAdvisor->viewActions); while (i.hasNext()) { i.next()->setEnabled(false); } if(windowAdvisor->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { windowAdvisor->openDicomEditorAction->setEnabled(false); } windowAdvisor->fileSaveProjectAction->setEnabled(false); windowAdvisor->closeProjectAction->setEnabled(false); windowAdvisor->undoAction->setEnabled(false); windowAdvisor->redoAction->setEnabled(false); windowAdvisor->imageNavigatorAction->setEnabled(false); windowAdvisor->viewNavigatorAction->setEnabled(false); windowAdvisor->resetPerspAction->setEnabled(false); if( windowAdvisor->GetShowClosePerspectiveMenuItem() ) { windowAdvisor->closePerspAction->setEnabled(false); } } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; bool perspectivesClosed; }; class PerspectiveListenerForMenu: public berry::IPerspectiveListener { public: PerspectiveListenerForMenu(QmitkExtWorkbenchWindowAdvisor* wa) : windowAdvisor(wa) { } Events::Types GetPerspectiveEventTypes() const override { return Events::ACTIVATED | Events::DEACTIVATED; } void PerspectiveActivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(true); } } void PerspectiveDeactivated(const berry::IWorkbenchPage::Pointer& /*page*/, const berry::IPerspectiveDescriptor::Pointer& perspective) override { QAction* action = windowAdvisor->mapPerspIdToAction[perspective->GetId()]; if (action) { action->setChecked(false); } } private: QmitkExtWorkbenchWindowAdvisor* windowAdvisor; }; QmitkExtWorkbenchWindowAdvisor::QmitkExtWorkbenchWindowAdvisor(berry::WorkbenchAdvisor* wbAdvisor, berry::IWorkbenchWindowConfigurer::Pointer configurer) : berry::WorkbenchWindowAdvisor(configurer), lastInput(nullptr), wbAdvisor(wbAdvisor), showViewToolbar(true), showPerspectiveToolbar(false), showVersionInfo(true), showMitkVersionInfo(true), showViewMenuItem(true), showNewWindowMenuItem(false), showClosePerspectiveMenuItem(true), viewNavigatorFound(false), showMemoryIndicator(true), dropTargetListener(new QmitkDefaultDropTargetListener) { productName = QCoreApplication::applicationName(); viewExcludeList.push_back("org.mitk.views.viewnavigatorview"); } QmitkExtWorkbenchWindowAdvisor::~QmitkExtWorkbenchWindowAdvisor() { } berry::ActionBarAdvisor::Pointer QmitkExtWorkbenchWindowAdvisor::CreateActionBarAdvisor( berry::IActionBarConfigurer::Pointer configurer) { if (USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { berry::ActionBarAdvisor::Pointer actionBarAdvisor( new QmitkExtActionBarAdvisor(configurer)); return actionBarAdvisor; } else { return berry::WorkbenchWindowAdvisor::CreateActionBarAdvisor(configurer); } } QWidget* QmitkExtWorkbenchWindowAdvisor::CreateEmptyWindowContents(QWidget* parent) { QWidget* parentWidget = static_cast(parent); auto label = new QLabel(parentWidget); label->setText("No perspectives are open. Open a perspective in the Window->Open Perspective menu."); label->setContentsMargins(10,10,10,10); label->setAlignment(Qt::AlignTop); label->setEnabled(false); parentWidget->layout()->addWidget(label); return label; } void QmitkExtWorkbenchWindowAdvisor::ShowClosePerspectiveMenuItem(bool show) { showClosePerspectiveMenuItem = show; } bool QmitkExtWorkbenchWindowAdvisor::GetShowClosePerspectiveMenuItem() { return showClosePerspectiveMenuItem; } void QmitkExtWorkbenchWindowAdvisor::ShowMemoryIndicator(bool show) { showMemoryIndicator = show; } bool QmitkExtWorkbenchWindowAdvisor::GetShowMemoryIndicator() { return showMemoryIndicator; } void QmitkExtWorkbenchWindowAdvisor::ShowNewWindowMenuItem(bool show) { showNewWindowMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewToolbar(bool show) { showViewToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowViewMenuItem(bool show) { showViewMenuItem = show; } void QmitkExtWorkbenchWindowAdvisor::ShowPerspectiveToolbar(bool show) { showPerspectiveToolbar = show; } void QmitkExtWorkbenchWindowAdvisor::ShowVersionInfo(bool show) { showVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::ShowMitkVersionInfo(bool show) { showMitkVersionInfo = show; } void QmitkExtWorkbenchWindowAdvisor::SetProductName(const QString& product) { productName = product; } void QmitkExtWorkbenchWindowAdvisor::SetWindowIcon(const QString& wndIcon) { windowIcon = wndIcon; } void QmitkExtWorkbenchWindowAdvisor::PostWindowCreate() { // very bad hack... berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = qobject_cast (window->GetShell()->GetControl()); if (!windowIcon.isEmpty()) { mainWindow->setWindowIcon(QIcon(windowIcon)); } mainWindow->setContextMenuPolicy(Qt::PreventContextMenu); /*mainWindow->setStyleSheet("color: white;" "background-color: #808080;" "selection-color: #659EC7;" "selection-background-color: #808080;" " QMenuBar {" "background-color: #808080; }");*/ // Load selected icon theme QStringList searchPaths = QIcon::themeSearchPaths(); searchPaths.push_front( QString(":/org_mitk_icons/icons/") ); QIcon::setThemeSearchPaths( searchPaths ); berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); berry::IPreferences::Pointer stylePref = prefService->GetSystemPreferences()->Node(berry::QtPreferences::QT_STYLES_NODE); QString iconTheme = stylePref->Get(berry::QtPreferences::QT_ICON_THEME, "<>"); if( iconTheme == QString( "<>" ) ) { iconTheme = QString( "tango" ); } QIcon::setThemeName( iconTheme ); // ==== Application menu ============================ QMenuBar* menuBar = mainWindow->menuBar(); menuBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ menuBar->setNativeMenuBar(true); #else menuBar->setNativeMenuBar(false); #endif QAction* fileOpenAction = new QmitkFileOpenAction(QIcon::fromTheme("document-open",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/document-open.svg")), window); fileOpenAction->setShortcut(QKeySequence::Open); QAction* fileSaveAction = new QmitkFileSaveAction(QIcon(":/org.mitk.gui.qt.ext/Save_48.png"), window); fileSaveAction->setShortcut(QKeySequence::Save); fileSaveProjectAction = new QmitkExtFileSaveProjectAction(window); fileSaveProjectAction->setIcon(QIcon::fromTheme("document-save",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/document-save.svg"))); closeProjectAction = new QmitkCloseProjectAction(window); closeProjectAction->setIcon(QIcon::fromTheme("edit-delete",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-delete.svg"))); auto perspGroup = new QActionGroup(menuBar); std::map VDMap; // sort elements (converting vector to map...) QList::const_iterator iter; berry::IViewRegistry* viewRegistry = berry::PlatformUI::GetWorkbench()->GetViewRegistry(); const QList viewDescriptors = viewRegistry->GetViews(); bool skip = false; for (iter = viewDescriptors.begin(); iter != viewDescriptors.end(); ++iter) { // if viewExcludeList is set, it contains the id-strings of view, which // should not appear as an menu-entry in the menu if (viewExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } if ((*iter)->GetId() == "org.blueberry.ui.internal.introview") continue; if ((*iter)->GetId() == "org.mitk.views.imagenavigator") continue; if ((*iter)->GetId() == "org.mitk.views.viewnavigatorview") continue; std::pair p( (*iter)->GetLabel(), (*iter)); VDMap.insert(p); } std::map::const_iterator MapIter; for (MapIter = VDMap.begin(); MapIter != VDMap.end(); ++MapIter) { berry::QtShowViewAction* viewAction = new berry::QtShowViewAction(window, (*MapIter).second); viewActions.push_back(viewAction); } if (!USE_EXPERIMENTAL_COMMAND_CONTRIBUTIONS) { QMenu* fileMenu = menuBar->addMenu("&File"); fileMenu->setObjectName("FileMenu"); fileMenu->addAction(fileOpenAction); fileMenu->addAction(fileSaveAction); fileMenu->addAction(fileSaveProjectAction); fileMenu->addAction(closeProjectAction); fileMenu->addSeparator(); QAction* fileExitAction = new QmitkFileExitAction(window); fileExitAction->setIcon(QIcon::fromTheme("system-log-out",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/system-log-out.svg"))); fileExitAction->setShortcut(QKeySequence::Quit); fileExitAction->setObjectName("QmitkFileExitAction"); fileMenu->addAction(fileExitAction); // another bad hack to get an edit/undo menu... QMenu* editMenu = menuBar->addMenu("&Edit"); undoAction = editMenu->addAction(QIcon::fromTheme("edit-undo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-undo.svg")), "&Undo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onUndo()), QKeySequence("CTRL+Z")); undoAction->setToolTip("Undo the last action (not supported by all modules)"); redoAction = editMenu->addAction(QIcon::fromTheme("edit-redo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-redo.svg")) , "&Redo", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onRedo()), QKeySequence("CTRL+Y")); redoAction->setToolTip("execute the last action that was undone again (not supported by all modules)"); // ==== Window Menu ========================== QMenu* windowMenu = menuBar->addMenu("Window"); if (showNewWindowMenuItem) { windowMenu->addAction("&New Window", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onNewWindow())); windowMenu->addSeparator(); } QMenu* perspMenu = windowMenu->addMenu("&Open Perspective"); - QMenu* viewMenu; + QMenu* viewMenu = nullptr; if (showViewMenuItem) { viewMenu = windowMenu->addMenu("Show &View"); viewMenu->setObjectName("Show View"); } windowMenu->addSeparator(); resetPerspAction = windowMenu->addAction("&Reset Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onResetPerspective())); if(showClosePerspectiveMenuItem) closePerspAction = windowMenu->addAction("&Close Perspective", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onClosePerspective())); windowMenu->addSeparator(); windowMenu->addAction("&Preferences...", QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onEditPreferences()), QKeySequence("CTRL+P")); // fill perspective menu berry::IPerspectiveRegistry* perspRegistry = window->GetWorkbench()->GetPerspectiveRegistry(); QList perspectives( perspRegistry->GetPerspectives()); skip = false; for (QList::iterator perspIt = perspectives.begin(); perspIt != perspectives.end(); ++perspIt) { // if perspectiveExcludeList is set, it contains the id-strings of perspectives, which // should not appear as an menu-entry in the perspective menu if (perspectiveExcludeList.size() > 0) { for (int i=0; iGetId()) { skip = true; break; } } if (skip) { skip = false; continue; } } QAction* perspAction = new berry::QtOpenPerspectiveAction(window, *perspIt, perspGroup); mapPerspIdToAction.insert((*perspIt)->GetId(), perspAction); } perspMenu->addActions(perspGroup->actions()); if (showViewMenuItem) { for (auto viewAction : viewActions) { viewMenu->addAction(viewAction); } } // ===== Help menu ==================================== QMenu* helpMenu = menuBar->addMenu("&Help"); helpMenu->addAction("&Welcome",this, SLOT(onIntro())); helpMenu->addAction("&Open Help Perspective", this, SLOT(onHelpOpenHelpPerspective())); helpMenu->addAction("&Context Help",this, SLOT(onHelp()), QKeySequence("F1")); helpMenu->addAction("&About",this, SLOT(onAbout())); // ===================================================== } else { //undoAction = new QAction(QIcon::fromTheme("edit-undo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-undo.svg")), // "&Undo", nullptr); undoAction = new QmitkUndoAction(QIcon::fromTheme("edit-undo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-undo.svg")), nullptr); undoAction->setShortcut(QKeySequence::Undo); redoAction = new QmitkRedoAction(QIcon::fromTheme("edit-redo",QIcon(":/org_mitk_icons/icons/tango/scalable/actions/edit-redo.svg")), nullptr); redoAction->setShortcut(QKeySequence::Redo); } // toolbar for showing file open, undo, redo and other main actions auto mainActionsToolBar = new QToolBar; mainActionsToolBar->setObjectName("mainActionsToolBar"); mainActionsToolBar->setContextMenuPolicy(Qt::PreventContextMenu); #ifdef __APPLE__ mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextUnderIcon ); #else mainActionsToolBar->setToolButtonStyle ( Qt::ToolButtonTextBesideIcon ); #endif imageNavigatorAction = new QAction(QIcon(":/org.mitk.gui.qt.ext/Slider.png"), "&Image Navigator", nullptr); bool imageNavigatorViewFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.imagenavigator"); if(this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { openDicomEditorAction = new QmitkOpenDicomEditorAction(QIcon(":/org.mitk.gui.qt.ext/dcm-icon.png"),window); } if (imageNavigatorViewFound) { QObject::connect(imageNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onImageNavigator())); imageNavigatorAction->setCheckable(true); // add part listener for image navigator imageNavigatorPartListener.reset(new PartListenerForImageNavigator(imageNavigatorAction)); window->GetPartService()->AddPartListener(imageNavigatorPartListener.data()); berry::IViewPart::Pointer imageNavigatorView = window->GetActivePage()->FindView("org.mitk.views.imagenavigator"); imageNavigatorAction->setChecked(false); if (imageNavigatorView) { bool isImageNavigatorVisible = window->GetActivePage()->IsPartVisible(imageNavigatorView); if (isImageNavigatorVisible) imageNavigatorAction->setChecked(true); } imageNavigatorAction->setToolTip("Toggle image navigator for navigating through image"); } viewNavigatorAction = new QAction(QIcon(":/org.mitk.gui.qt.ext/view-manager_48.png"),"&View Navigator", nullptr); viewNavigatorFound = window->GetWorkbench()->GetViewRegistry()->Find("org.mitk.views.viewnavigatorview"); if (viewNavigatorFound) { QObject::connect(viewNavigatorAction, SIGNAL(triggered(bool)), QmitkExtWorkbenchWindowAdvisorHack::undohack, SLOT(onViewNavigator())); viewNavigatorAction->setCheckable(true); // add part listener for view navigator viewNavigatorPartListener.reset(new PartListenerForViewNavigator(viewNavigatorAction)); window->GetPartService()->AddPartListener(viewNavigatorPartListener.data()); berry::IViewPart::Pointer viewnavigatorview = window->GetActivePage()->FindView("org.mitk.views.viewnavigatorview"); viewNavigatorAction->setChecked(false); if (viewnavigatorview) { bool isViewNavigatorVisible = window->GetActivePage()->IsPartVisible(viewnavigatorview); if (isViewNavigatorVisible) viewNavigatorAction->setChecked(true); } viewNavigatorAction->setToolTip("Toggle View Navigator"); } mainActionsToolBar->addAction(fileOpenAction); mainActionsToolBar->addAction(fileSaveProjectAction); mainActionsToolBar->addAction(closeProjectAction); mainActionsToolBar->addAction(undoAction); mainActionsToolBar->addAction(redoAction); if(this->GetWindowConfigurer()->GetWindow()->GetWorkbench()->GetEditorRegistry()->FindEditor("org.mitk.editors.dicomeditor")) { mainActionsToolBar->addAction(openDicomEditorAction); } if (imageNavigatorViewFound) { mainActionsToolBar->addAction(imageNavigatorAction); } if (viewNavigatorFound) { mainActionsToolBar->addAction(viewNavigatorAction); } mainWindow->addToolBar(mainActionsToolBar); // ==== Perspective Toolbar ================================== auto qPerspectiveToolbar = new QToolBar; qPerspectiveToolbar->setObjectName("perspectiveToolBar"); if (showPerspectiveToolbar) { qPerspectiveToolbar->addActions(perspGroup->actions()); mainWindow->addToolBar(qPerspectiveToolbar); } else delete qPerspectiveToolbar; // ==== View Toolbar ================================== auto qToolbar = new QToolBar; qToolbar->setObjectName("viewToolBar"); if (showViewToolbar) { mainWindow->addToolBar(qToolbar); for (auto viewAction : viewActions) { qToolbar->addAction(viewAction); } } else delete qToolbar; QSettings settings(GetQSettingsFile(), QSettings::IniFormat); mainWindow->restoreState(settings.value("ToolbarPosition").toByteArray()); auto qStatusBar = new QStatusBar(); //creating a QmitkStatusBar for Output on the QStatusBar and connecting it with the MainStatusBar auto statusBar = new QmitkStatusBar(qStatusBar); //disabling the SizeGrip in the lower right corner statusBar->SetSizeGripEnabled(false); auto progBar = new QmitkProgressBar(); qStatusBar->addPermanentWidget(progBar, 0); progBar->hide(); // progBar->AddStepsToDo(2); // progBar->Progress(1); mainWindow->setStatusBar(qStatusBar); if (showMemoryIndicator) { auto memoryIndicator = new QmitkMemoryUsageIndicatorView(); qStatusBar->addPermanentWidget(memoryIndicator, 0); } } void QmitkExtWorkbenchWindowAdvisor::PreWindowOpen() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); // show the shortcut bar and progress indicator, which are hidden by // default //configurer->SetShowPerspectiveBar(true); //configurer->SetShowFastViewBars(true); //configurer->SetShowProgressIndicator(true); // // add the drag and drop support for the editor area // configurer.addEditorAreaTransfer(EditorInputTransfer.getInstance()); // configurer.addEditorAreaTransfer(ResourceTransfer.getInstance()); // configurer.addEditorAreaTransfer(FileTransfer.getInstance()); // configurer.addEditorAreaTransfer(MarkerTransfer.getInstance()); // configurer.configureEditorAreaDropListener(new EditorAreaDropAdapter( // configurer.getWindow())); this->HookTitleUpdateListeners(configurer); menuPerspectiveListener.reset(new PerspectiveListenerForMenu(this)); configurer->GetWindow()->AddPerspectiveListener(menuPerspectiveListener.data()); configurer->AddEditorAreaTransfer(QStringList("text/uri-list")); configurer->ConfigureEditorAreaDropListener(dropTargetListener.data()); } void QmitkExtWorkbenchWindowAdvisor::PostWindowOpen() { berry::WorkbenchWindowAdvisor::PostWindowOpen(); // Force Rendering Window Creation on startup. berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); ctkServiceReference serviceRef = context->getServiceReference(); if (serviceRef) { mitk::IDataStorageService *dsService = context->getService(serviceRef); if (dsService) { mitk::IDataStorageReference::Pointer dsRef = dsService->GetDataStorage(); mitk::DataStorageEditorInput::Pointer dsInput(new mitk::DataStorageEditorInput(dsRef)); mitk::WorkbenchUtil::OpenEditor(configurer->GetWindow()->GetActivePage(),dsInput); } } } void QmitkExtWorkbenchWindowAdvisor::onIntro() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onIntro(); } void QmitkExtWorkbenchWindowAdvisor::onHelp() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelp(); } void QmitkExtWorkbenchWindowAdvisor::onHelpOpenHelpPerspective() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onHelpOpenHelpPerspective(); } void QmitkExtWorkbenchWindowAdvisor::onAbout() { QmitkExtWorkbenchWindowAdvisorHack::undohack->onAbout(); } //-------------------------------------------------------------------------------- // Ugly hack from here on. Feel free to delete when command framework // and undo buttons are done. //-------------------------------------------------------------------------------- QmitkExtWorkbenchWindowAdvisorHack::QmitkExtWorkbenchWindowAdvisorHack() : QObject() { } QmitkExtWorkbenchWindowAdvisorHack::~QmitkExtWorkbenchWindowAdvisorHack() { } void QmitkExtWorkbenchWindowAdvisorHack::onUndo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetUndoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Undo " << descriptions.front().second; } } model->Undo(); } else { MITK_ERROR << "No undo model instantiated"; } } void QmitkExtWorkbenchWindowAdvisorHack::onRedo() { mitk::UndoModel* model = mitk::UndoController::GetCurrentUndoModel(); if (model) { if (mitk::VerboseLimitedLinearUndo* verboseundo = dynamic_cast( model )) { mitk::VerboseLimitedLinearUndo::StackDescription descriptions = verboseundo->GetRedoDescriptions(); if (descriptions.size() >= 1) { MITK_INFO << "Redo " << descriptions.front().second; } } model->Redo(); } else { MITK_ERROR << "No undo model instantiated"; } } // safe calls to the complete chain // berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->FindView("org.mitk.views.imagenavigator"); // to cover for all possible cases of closed pages etc. static void SafeHandleNavigatorView(QString view_query_name) { berry::IWorkbench* wbench = berry::PlatformUI::GetWorkbench(); if( wbench == nullptr ) return; berry::IWorkbenchWindow::Pointer wbench_window = wbench->GetActiveWorkbenchWindow(); if( wbench_window.IsNull() ) return; berry::IWorkbenchPage::Pointer wbench_page = wbench_window->GetActivePage(); if( wbench_page.IsNull() ) return; auto wbench_view = wbench_page->FindView( view_query_name ); if( wbench_view.IsNotNull() ) { bool isViewVisible = wbench_page->IsPartVisible( wbench_view ); if( isViewVisible ) { wbench_page->HideView( wbench_view ); return; } } wbench_page->ShowView( view_query_name ); } void QmitkExtWorkbenchWindowAdvisorHack::onImageNavigator() { // show/hide ImageNavigatorView SafeHandleNavigatorView("org.mitk.views.imagenavigator"); } void QmitkExtWorkbenchWindowAdvisorHack::onViewNavigator() { // show/hide viewnavigatorView SafeHandleNavigatorView("org.mitk.views.viewnavigatorview"); } void QmitkExtWorkbenchWindowAdvisorHack::onEditPreferences() { QmitkPreferencesDialog _PreferencesDialog(QApplication::activeWindow()); _PreferencesDialog.exec(); } void QmitkExtWorkbenchWindowAdvisorHack::onQuit() { berry::PlatformUI::GetWorkbench()->Close(); } void QmitkExtWorkbenchWindowAdvisorHack::onResetPerspective() { berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage()->ResetPerspective(); } void QmitkExtWorkbenchWindowAdvisorHack::onClosePerspective() { berry::IWorkbenchPage::Pointer page = berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()->GetActivePage(); page->ClosePerspective(page->GetPerspective(), true, true); } void QmitkExtWorkbenchWindowAdvisorHack::onNewWindow() { berry::PlatformUI::GetWorkbench()->OpenWorkbenchWindow(nullptr); } void QmitkExtWorkbenchWindowAdvisorHack::onIntro() { bool hasIntro = berry::PlatformUI::GetWorkbench()->GetIntroManager()->HasIntro(); if (!hasIntro) { QRegExp reg("(.*)(\\n)*"); QRegExp reg2("(\\n)*(.*)"); QFile file(":/org.mitk.gui.qt.ext/index.html"); file.open(QIODevice::ReadOnly | QIODevice::Text); //text file only for reading QString text = QString(file.readAll()); file.close(); QString title = text; title.replace(reg, ""); title.replace(reg2, ""); std::cout << title.toStdString() << std::endl; QMessageBox::information(nullptr, title, text, "Close"); } else { berry::PlatformUI::GetWorkbench()->GetIntroManager()->ShowIntro( berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow(), false); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelp() { ctkPluginContext* context = QmitkCommonExtPlugin::getContext(); if (context == nullptr) { MITK_WARN << "Plugin context not set, unable to open context help"; return; } // Check if the org.blueberry.ui.qt.help plug-in is installed and started QList > plugins = context->getPlugins(); foreach(QSharedPointer p, plugins) { if (p->getSymbolicName() == "org.blueberry.ui.qt.help") { if (p->getState() != ctkPlugin::ACTIVE) { // try to activate the plug-in explicitly try { p->start(ctkPlugin::START_TRANSIENT); } catch (const ctkPluginException& pe) { MITK_ERROR << "Activating org.blueberry.ui.qt.help failed: " << pe.what(); return; } } } } ctkServiceReference eventAdminRef = context->getServiceReference(); ctkEventAdmin* eventAdmin = nullptr; if (eventAdminRef) { eventAdmin = context->getService(eventAdminRef); } if (eventAdmin == nullptr) { MITK_WARN << "ctkEventAdmin service not found. Unable to open context help"; } else { ctkEvent ev("org/blueberry/ui/help/CONTEXTHELP_REQUESTED"); eventAdmin->postEvent(ev); } } void QmitkExtWorkbenchWindowAdvisorHack::onHelpOpenHelpPerspective() { berry::PlatformUI::GetWorkbench()->ShowPerspective("org.blueberry.perspectives.help", berry::PlatformUI::GetWorkbench()->GetActiveWorkbenchWindow()); } void QmitkExtWorkbenchWindowAdvisorHack::onAbout() { auto aboutDialog = new QmitkAboutDialog(QApplication::activeWindow(),nullptr); aboutDialog->open(); } void QmitkExtWorkbenchWindowAdvisor::HookTitleUpdateListeners( berry::IWorkbenchWindowConfigurer::Pointer configurer) { // hook up the listeners to update the window title titlePartListener.reset(new PartListenerForTitle(this)); titlePerspectiveListener.reset(new PerspectiveListenerForTitle(this)); editorPropertyListener.reset(new berry::PropertyChangeIntAdapter< QmitkExtWorkbenchWindowAdvisor>(this, &QmitkExtWorkbenchWindowAdvisor::PropertyChange)); // configurer.getWindow().addPageListener(new IPageListener() { // public void pageActivated(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageClosed(IWorkbenchPage page) { // updateTitle(false); // } // // public void pageOpened(IWorkbenchPage page) { // // do nothing // } // }); configurer->GetWindow()->AddPerspectiveListener(titlePerspectiveListener.data()); configurer->GetWindow()->GetPartService()->AddPartListener(titlePartListener.data()); } QString QmitkExtWorkbenchWindowAdvisor::ComputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchPage::Pointer currentPage = configurer->GetWindow()->GetActivePage(); berry::IEditorPart::Pointer activeEditor; if (currentPage) { activeEditor = lastActiveEditor.Lock(); } QString title; berry::IProduct::Pointer product = berry::Platform::GetProduct(); if (product.IsNotNull()) { title = product->GetName(); } if (title.isEmpty()) { // instead of the product name, we use a custom variable for now title = productName; } if(showMitkVersionInfo) { title += QString(" ") + MITK_VERSION_STRING; } if (showVersionInfo) { // add version informatioin QString versions = QString(" (ITK %1.%2.%3 VTK %4.%5.%6 Qt %7 MITK %8)") .arg(ITK_VERSION_MAJOR).arg(ITK_VERSION_MINOR).arg(ITK_VERSION_PATCH) .arg(VTK_MAJOR_VERSION).arg(VTK_MINOR_VERSION).arg(VTK_BUILD_VERSION) .arg(QT_VERSION_STR) .arg(MITK_VERSION_STRING); title += versions; } if (currentPage) { if (activeEditor) { lastEditorTitle = activeEditor->GetTitleToolTip(); if (!lastEditorTitle.isEmpty()) title = lastEditorTitle + " - " + title; } berry::IPerspectiveDescriptor::Pointer persp = currentPage->GetPerspective(); QString label = ""; if (persp) { label = persp->GetLabel(); } berry::IAdaptable* input = currentPage->GetInput(); if (input && input != wbAdvisor->GetDefaultPageInput()) { label = currentPage->GetLabel(); } if (!label.isEmpty()) { title = label + " - " + title; } } title += " (Not for use in diagnosis or treatment of patients)"; return title; } void QmitkExtWorkbenchWindowAdvisor::RecomputeTitle() { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); QString oldTitle = configurer->GetTitle(); QString newTitle = ComputeTitle(); if (newTitle != oldTitle) { configurer->SetTitle(newTitle); } } void QmitkExtWorkbenchWindowAdvisor::UpdateTitle(bool editorHidden) { berry::IWorkbenchWindowConfigurer::Pointer configurer = GetWindowConfigurer(); berry::IWorkbenchWindow::Pointer window = configurer->GetWindow(); berry::IEditorPart::Pointer activeEditor; berry::IWorkbenchPage::Pointer currentPage = window->GetActivePage(); berry::IPerspectiveDescriptor::Pointer persp; berry::IAdaptable* input = nullptr; if (currentPage) { activeEditor = currentPage->GetActiveEditor(); persp = currentPage->GetPerspective(); input = currentPage->GetInput(); } if (editorHidden) { activeEditor = nullptr; } // Nothing to do if the editor hasn't changed if (activeEditor == lastActiveEditor.Lock() && currentPage == lastActivePage.Lock() && persp == lastPerspective.Lock() && input == lastInput) { return; } if (!lastActiveEditor.Expired()) { lastActiveEditor.Lock()->RemovePropertyListener(editorPropertyListener.data()); } lastActiveEditor = activeEditor; lastActivePage = currentPage; lastPerspective = persp; lastInput = input; if (activeEditor) { activeEditor->AddPropertyListener(editorPropertyListener.data()); } RecomputeTitle(); } void QmitkExtWorkbenchWindowAdvisor::PropertyChange(const berry::Object::Pointer& /*source*/, int propId) { if (propId == berry::IWorkbenchPartConstants::PROP_TITLE) { if (!lastActiveEditor.Expired()) { QString newTitle = lastActiveEditor.Lock()->GetPartName(); if (lastEditorTitle != newTitle) { RecomputeTitle(); } } } } void QmitkExtWorkbenchWindowAdvisor::SetPerspectiveExcludeList(const QList& v) { this->perspectiveExcludeList = v; } QList QmitkExtWorkbenchWindowAdvisor::GetPerspectiveExcludeList() { return this->perspectiveExcludeList; } void QmitkExtWorkbenchWindowAdvisor::SetViewExcludeList(const QList& v) { this->viewExcludeList = v; } QList QmitkExtWorkbenchWindowAdvisor::GetViewExcludeList() { return this->viewExcludeList; } void QmitkExtWorkbenchWindowAdvisor::PostWindowClose() { berry::IWorkbenchWindow::Pointer window = this->GetWindowConfigurer()->GetWindow(); QMainWindow* mainWindow = static_cast (window->GetShell()->GetControl()); QSettings settings(GetQSettingsFile(), QSettings::IniFormat); settings.setValue("ToolbarPosition", mainWindow->saveState()); } QString QmitkExtWorkbenchWindowAdvisor::GetQSettingsFile() const { QFileInfo settingsInfo = QmitkCommonExtPlugin::getContext()->getDataFile(QT_SETTINGS_FILENAME); return settingsInfo.canonicalFilePath(); }