diff --git a/Modules/PhotoacousticsLib/include/mitkPASpectralUnmixingFilterVigra.h b/Modules/PhotoacousticsLib/include/mitkPASpectralUnmixingFilterVigra.h index 4f4db58c31..54b2f65fd0 100644 --- a/Modules/PhotoacousticsLib/include/mitkPASpectralUnmixingFilterVigra.h +++ b/Modules/PhotoacousticsLib/include/mitkPASpectralUnmixingFilterVigra.h @@ -1,56 +1,57 @@ /*=================================================================== 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 MITKPHOTOACOUSTICSPECTRALUNMIXINGFILTERVIGRA_H #define MITKPHOTOACOUSTICSPECTRALUNMIXINGFILTERVIGRA_H #include #include #include namespace mitk { namespace pa { class MITKPHOTOACOUSTICSLIB_EXPORT SpectralUnmixingFilterVigra : public SpectralUnmixingFilterBase { public: mitkClassMacro(SpectralUnmixingFilterVigra, SpectralUnmixingFilterBase) itkFactorylessNewMacro(Self) - enum VigraAlgortihmType + enum VigraAlgortihmType { LARS, GOLDFARB, WEIGHTED, + LS, vigratest }; void mitk::pa::SpectralUnmixingFilterVigra::SetAlgorithm(VigraAlgortihmType SetAlgorithmIndex); protected: SpectralUnmixingFilterVigra(); virtual ~SpectralUnmixingFilterVigra(); virtual Eigen::VectorXf SpectralUnmixingAlgorithm(Eigen::Matrix EndmemberMatrix, Eigen::VectorXf inputVector) override; private: mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType algorithmIndex; }; } } #endif // MITKPHOTOACOUSTICSPECTRALUNMIXINGFILTERVIGRA_H diff --git a/Modules/PhotoacousticsLib/src/SUFilter/mitkPASpectralUnmixingFilterVigra.cpp b/Modules/PhotoacousticsLib/src/SUFilter/mitkPASpectralUnmixingFilterVigra.cpp index 1107d33bdb..345b97dd4c 100644 --- a/Modules/PhotoacousticsLib/src/SUFilter/mitkPASpectralUnmixingFilterVigra.cpp +++ b/Modules/PhotoacousticsLib/src/SUFilter/mitkPASpectralUnmixingFilterVigra.cpp @@ -1,148 +1,150 @@ /*=================================================================== 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 "mitkPASpectralUnmixingFilterVigra.h" // Includes for AddEnmemberMatrix #include "mitkPAPropertyCalculator.h" #include // Testing algorithms #include // ImageAccessor #include #include //vigra /* The VIGRA License ================= (identical to the MIT X11 License) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include #include #include mitk::pa::SpectralUnmixingFilterVigra::SpectralUnmixingFilterVigra() { } mitk::pa::SpectralUnmixingFilterVigra::~SpectralUnmixingFilterVigra() { } void mitk::pa::SpectralUnmixingFilterVigra::SetAlgorithm(mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType SetAlgorithmIndex) { algorithmIndex = SetAlgorithmIndex; } Eigen::VectorXf mitk::pa::SpectralUnmixingFilterVigra::SpectralUnmixingAlgorithm( Eigen::Matrix EndmemberMatrix, Eigen::VectorXf inputVector) { - Eigen::VectorXf resultVector; int numberOfWavelengths = EndmemberMatrix.rows(); int numberOfChromophores = EndmemberMatrix.cols(); + Eigen::VectorXf resultVector(numberOfChromophores); using namespace vigra; using namespace vigra::linalg; std::vector A_data; std::vector B_data; std::vector weightsvec = { 1,1,1,1,1 }; //am besten über GUI Eingabe festlegen for (int i = 0; i < numberOfWavelengths; ++i) { B_data.push_back(inputVector(i)); for (int j = 0; j < numberOfChromophores; ++j) { A_data.push_back(EndmemberMatrix(i, j)); } } const double* Adat = A_data.data(); const double* Bdat = B_data.data(); vigra::Matrix A(Shape2(numberOfWavelengths, numberOfChromophores), Adat); vigra::Matrix b(Shape2(numberOfWavelengths, 1), Bdat); vigra::Matrix x(Shape2(numberOfChromophores, 1)); // minimize (A*x-b)^2 s.t. x>=0 using least angle regression (LARS algorithm) if (mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::LARS == algorithmIndex) nonnegativeLeastSquares(A, b, x); else if (mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::GOLDFARB == algorithmIndex) { vigra::Matrix eye(identityMatrix(numberOfChromophores)), zeros(Shape2(numberOfChromophores, 1)), empty, U = transpose(A)*A, v = -transpose(A)*b; x = 0; // minimize (A*x-b)^2 s.t. x>=0 using the Goldfarb-Idnani algorithm quadraticProgramming(U, v, empty, empty, eye, zeros, x); } else if (mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::WEIGHTED == algorithmIndex) { std::vector weightsvec = { 1,1,1,1,1 }; //am besten über GUI Eingabe festlegen const double* weightsdat = weightsvec.data(); vigra::Matrix weigths(Shape2(numberOfWavelengths, 1), weightsdat); vigra::linalg::weightedLeastSquares(A, b, weigths, x); } + else if (mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::LS == algorithmIndex) + linearSolve(A, b, x); + else if (mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::vigratest == algorithmIndex) linearSolve(A, b, x); - //mitkThrow() << "nothing implemented"; + //mitkThrow() << "nothing implemented"; else mitkThrow() << "404 VIGRA ALGORITHM NOT FOUND"; - Eigen::VectorXf result(numberOfChromophores); for (int k = 0; k < numberOfChromophores; ++k) { float test = x(k, 0); - result[k] = test; + resultVector[k] = test; } return resultVector; } diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.spectralunmixing/src/internal/SpectralUnmixing.cpp b/Plugins/org.mitk.gui.qt.photoacoustics.spectralunmixing/src/internal/SpectralUnmixing.cpp index fd421e5a5a..a4aa3d27a9 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.spectralunmixing/src/internal/SpectralUnmixing.cpp +++ b/Plugins/org.mitk.gui.qt.photoacoustics.spectralunmixing/src/internal/SpectralUnmixing.cpp @@ -1,360 +1,367 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ // Blueberry #include #include // Qmitk #include "SpectralUnmixing.h" // Qt #include // mitk image #include // Include to perform Spectral Unmixing #include "mitkPASpectralUnmixingFilterBase.h" #include "mitkPALinearSpectralUnmixingFilter.h" #include "mitkPASpectralUnmixingSO2.h" #include "mitkPASpectralUnmixingFilterVigra.h" #include "mitkPASpectralUnmixingFilterLagrange.h" #include "mitkPASpectralUnmixingFilterSimplex.h" const std::string SpectralUnmixing::VIEW_ID = "org.mitk.views.spectralunmixing"; void SpectralUnmixing::SetFocus() { m_Controls.buttonPerformImageProcessing->setFocus(); } void SpectralUnmixing::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); connect(m_Controls.buttonPerformImageProcessing, &QPushButton::clicked, this, &SpectralUnmixing::DoImageProcessing); } void SpectralUnmixing::ClearWavelength() { m_Wavelengths.clear(); } void SpectralUnmixing::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList &nodes) { // iterate all selected objects, adjust warning visibility foreach (mitk::DataNode::Pointer node, nodes) { if (node.IsNotNull() && dynamic_cast(node->GetData())) { m_Controls.labelWarning->setVisible(false); m_Controls.buttonPerformImageProcessing->setEnabled(true); return; } } m_Controls.labelWarning->setVisible(true); m_Controls.buttonPerformImageProcessing->setEnabled(false); } void SpectralUnmixing::DoImageProcessing() { QList nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode *node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(nullptr, "Template", "Please load and select an image before starting image processing."); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData *data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image *image = dynamic_cast(data); if (image) { std::stringstream message; std::string name; message << "PERFORMING SPECTRAL UNMIXING "; if (node->GetName(name)) { // a property called "name" was found for this DataNode message << "'" << name << "'"; } message << "."; MITK_INFO << message.str(); //Tranfer GUI information(Algortihm) to filter auto qs = m_Controls.QComboBoxAlgorithm->currentText(); std::string Algorithm = qs.toUtf8().constData(); mitk::pa::SpectralUnmixingFilterBase::Pointer m_SpectralUnmixingFilter; if (Algorithm == "householderQr") { m_SpectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::householderQr); } else if (Algorithm == "ldlt") { m_SpectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::ldlt); } else if (Algorithm == "llt") { m_SpectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::llt); } else if (Algorithm == "colPivHouseholderQr") { m_SpectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::colPivHouseholderQr); } else if (Algorithm == "jacobiSvd") { m_SpectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::jacobiSvd); } else if (Algorithm == "fullPivLu") { m_SpectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::fullPivLu); } else if (Algorithm == "fullPivHouseholderQr") { m_SpectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::fullPivHouseholderQr); } else if (Algorithm == "test") { m_SpectralUnmixingFilter = mitk::pa::LinearSpectralUnmixingFilter::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::LinearSpectralUnmixingFilter::AlgortihmType::test); } else if (Algorithm == "NNLARS") { m_SpectralUnmixingFilter = mitk::pa::SpectralUnmixingFilterVigra::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::LARS); } else if (Algorithm == "NNGoldfarb") { m_SpectralUnmixingFilter = mitk::pa::SpectralUnmixingFilterVigra::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::GOLDFARB); } else if (Algorithm == "weighted") { m_SpectralUnmixingFilter = mitk::pa::SpectralUnmixingFilterVigra::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::WEIGHTED); } + else if (Algorithm == "LS") + { + m_SpectralUnmixingFilter = mitk::pa::SpectralUnmixingFilterVigra::New(); + dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) + ->SetAlgorithm(mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::LS); + } + else if (Algorithm == "vigratest") { m_SpectralUnmixingFilter = mitk::pa::SpectralUnmixingFilterVigra::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()) ->SetAlgorithm(mitk::pa::SpectralUnmixingFilterVigra::VigraAlgortihmType::vigratest); } else if (Algorithm == "SimplexMax") { m_SpectralUnmixingFilter = mitk::pa::SpectralUnmixingFilterSimplex::New(); dynamic_cast(m_SpectralUnmixingFilter.GetPointer()); } else mitkThrow() << "404 ALGORITHM NOT FOUND!"; m_SpectralUnmixingFilter->SetInput(image); //Tranfer GUI information(Wavelength) to filter ClearWavelength(); int col = 0; int Wavelength = 1; while (m_Controls.inputtable->item(0, col) && Wavelength > 0) { QString Text = m_Controls.inputtable->item(0, col)->text(); Wavelength = Text.toInt(); if (Wavelength > 0) m_SpectralUnmixingFilter->AddWavelength(Wavelength); ++col; } //Tranfer GUI information(Chromophores) to filter unsigned int numberofChromophores = 0; DeOxbool = m_Controls.checkBoxDeOx->isChecked(); Oxbool = m_Controls.checkBoxOx->isChecked(); bool Melaninbool = m_Controls.checkBoxMelanin->isChecked(); bool Onebool = m_Controls.checkBoxAdd->isChecked(); if (DeOxbool || Oxbool) { MITK_INFO << "CHOSEN CHROMOPHORES:"; } if (Oxbool) { numberofChromophores += 1; MITK_INFO << "- Oxyhemoglobin"; m_SpectralUnmixingFilter->AddChromophore( mitk::pa::PropertyCalculator::ChromophoreType::OXYGENATED); } if (DeOxbool) { numberofChromophores += 1; MITK_INFO << "- Deoxygenated hemoglobin"; m_SpectralUnmixingFilter->AddChromophore( mitk::pa::PropertyCalculator::ChromophoreType::DEOXYGENATED); } if (Melaninbool) { numberofChromophores += 1; MITK_INFO << "- Melanin"; m_SpectralUnmixingFilter->AddChromophore( mitk::pa::PropertyCalculator::ChromophoreType::MELANIN); } if (Onebool) { numberofChromophores += 1; MITK_INFO << "- Additional Chromophore"; m_SpectralUnmixingFilter->AddChromophore( mitk::pa::PropertyCalculator::ChromophoreType::ONEENDMEMBER); } if (numberofChromophores == 0) { mitkThrow() << "PRESS 'IGNORE' AND CHOOSE A CHROMOPHORE!"; } MITK_INFO << "Updating Filter..."; m_SpectralUnmixingFilter->Update(); int outputCounter = 0; // Write Output images to Data Storage if (Oxbool) { mitk::Image::Pointer HbO2 = m_SpectralUnmixingFilter->GetOutput(outputCounter); outputCounter += 1; mitk::DataNode::Pointer dataNodeHbO2 = mitk::DataNode::New(); dataNodeHbO2->SetData(HbO2); dataNodeHbO2->SetName("HbO2 " + Algorithm); this->GetDataStorage()->Add(dataNodeHbO2); } if (DeOxbool) { mitk::Image::Pointer Hb = m_SpectralUnmixingFilter->GetOutput(outputCounter); outputCounter += 1; mitk::DataNode::Pointer dataNodeHb = mitk::DataNode::New(); dataNodeHb->SetData(Hb); dataNodeHb->SetName("Hb " + Algorithm); this->GetDataStorage()->Add(dataNodeHb); } if (Melaninbool) { mitk::Image::Pointer Melanin = m_SpectralUnmixingFilter->GetOutput(outputCounter); outputCounter += 1; mitk::DataNode::Pointer dataNodeMelanin = mitk::DataNode::New(); dataNodeMelanin->SetData(Melanin); dataNodeMelanin->SetName("Melanin " + Algorithm); this->GetDataStorage()->Add(dataNodeMelanin); } if (Onebool) { mitk::Image::Pointer One = m_SpectralUnmixingFilter->GetOutput(outputCounter); mitk::DataNode::Pointer dataNodeOne = mitk::DataNode::New(); dataNodeOne->SetData(One); dataNodeOne->SetName("One " + Algorithm); this->GetDataStorage()->Add(dataNodeOne); } //Calculate oxygen saturation bool sO2bool = m_Controls.checkBoxsO2->isChecked(); if (sO2bool) { if (!DeOxbool) mitkThrow() << "SELECT CHROMOPHORE DEOXYHEMOGLOBIN!"; if (!Oxbool) mitkThrow() << "SELECT CHROMOPHORE OXYHEMOGLOBIN!"; MITK_INFO << "CALCULATE OXYGEN SATURATION ..."; auto m_sO2 = mitk::pa::SpectralUnmixingSO2::New(); // Oxygen Saturation Setting for (int i = 0; i < 4; ++i) { if (m_Controls.inputtable->item(0, i)) { QString Text = m_Controls.tableSO2->item(0, i)->text(); float value = Text.toFloat(); MITK_INFO << "value: " << value; m_sO2->AddSO2Settings(value); } else m_sO2->AddSO2Settings(0); } // Initialize pipeline from SU filter class to SO2 class auto output1 = m_SpectralUnmixingFilter->GetOutput(0); auto output2 = m_SpectralUnmixingFilter->GetOutput(1); m_sO2->SetInput(0, output1); m_sO2->SetInput(1, output2); m_sO2->Update(); // Write Output images to Data Storage mitk::Image::Pointer sO2 = m_sO2->GetOutput(0); mitk::DataNode::Pointer dataNodesO2 = mitk::DataNode::New(); dataNodesO2->SetData(sO2); dataNodesO2->SetName("sO2"); this->GetDataStorage()->Add(dataNodesO2); MITK_INFO << "[DONE]"; } mitk::RenderingManager::GetInstance()->InitializeViewsByBoundingObjects(this->GetDataStorage()); MITK_INFO << "Adding images to DataStorage...[DONE]"; } } } diff --git a/Plugins/org.mitk.gui.qt.photoacoustics.spectralunmixing/src/internal/SpectralUnmixingControls.ui b/Plugins/org.mitk.gui.qt.photoacoustics.spectralunmixing/src/internal/SpectralUnmixingControls.ui index 32f2be545a..8e8067b944 100644 --- a/Plugins/org.mitk.gui.qt.photoacoustics.spectralunmixing/src/internal/SpectralUnmixingControls.ui +++ b/Plugins/org.mitk.gui.qt.photoacoustics.spectralunmixing/src/internal/SpectralUnmixingControls.ui @@ -1,842 +1,847 @@ SpectralUnmixingControls 0 0 398 575 0 0 QmitkTemplate QLabel { color: rgb(255, 0, 0) } Please select an image! Qt::Horizontal Do image processing Perform spectral unmixing Qt::Horizontal 75 true Wavelengths settings 0 75 16777215 75 10 true Qt::SolidLine 42 50 30 30 λ/nm Weights 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 720 755 831 906 942 Qt::Horizontal 75 true Chromophore settings Oxyhemoglobin true true Deoxygenated hemoglobin true true false Melanin Additional Endmember Qt::Horizontal 75 true Unmixing algorithm true MS Shell Dlg 2 false 21 2147483647 ==CHOSE ALGORITHM== ==QR decomposition== householderQr colPivHouseholderQr fullPivHouseholderQr ==LU decompositon fullPivLu ==Cholesky decompostion== ldlt llt .. ==Least squares== + + + LS + + jacobiSvd NNLARS NNGoldfarb weighted ==Others== SimplexMax Lagrange ==Test algorithms== test vigratest Qt::Horizontal 75 true Oxygen saturation calculate sO2 false 380 82 Value Min Hb Min HbO2 Min Sum Min SO2 % 100 200 200 50 Qt::Horizontal Qt::Vertical 20 69