diff --git a/CMakeExternals/GDCM.cmake b/CMakeExternals/GDCM.cmake index ae0c5cf59a..a8d1577792 100644 --- a/CMakeExternals/GDCM.cmake +++ b/CMakeExternals/GDCM.cmake @@ -1,70 +1,71 @@ #----------------------------------------------------------------------------- # GDCM #----------------------------------------------------------------------------- # Sanity checks if(DEFINED GDCM_DIR AND NOT EXISTS ${GDCM_DIR}) message(FATAL_ERROR "GDCM_DIR variable is defined but corresponds to non-existing directory") endif() # Check if an external ITK build tree was specified. # If yes, use the GDCM from ITK, otherwise ITK will complain if(ITK_DIR) find_package(ITK) if(ITK_GDCM_DIR) set(GDCM_DIR ${ITK_GDCM_DIR}) endif() endif() set(proj GDCM) set(proj_DEPENDENCIES ) set(GDCM_DEPENDS ${proj}) if(NOT DEFINED GDCM_DIR) set(additional_args ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() # On Mac some assertions fail that prevent reading certain DICOM files. Bug #19995 if(APPLE) list(APPEND additional_args "-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} -DNDEBUG" ) endif() mitk_query_custom_ep_vars() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/gdcm-3.0.4.tar.gz URL_MD5 f12dbded708356d5fa0b5ed37ccdb66e CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_args} -DGDCM_BUILD_SHARED_LIBS:BOOL=ON + -DGDCM_BUILD_DOCBOOK_MANPAGES:BOOL=OFF ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(GDCM_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") find_package(GDCM) endif() diff --git a/CMakeExternals/OpenCV.cmake b/CMakeExternals/OpenCV.cmake index b26c86a806..2a1e7ceb87 100644 --- a/CMakeExternals/OpenCV.cmake +++ b/CMakeExternals/OpenCV.cmake @@ -1,84 +1,85 @@ #----------------------------------------------------------------------------- # OpenCV #----------------------------------------------------------------------------- if(MITK_USE_OpenCV) # Sanity checks if(DEFINED OpenCV_DIR AND NOT EXISTS ${OpenCV_DIR}) message(FATAL_ERROR "OpenCV_DIR variable is defined but corresponds to non-existing directory") endif() set(proj OpenCV) set(proj_DEPENDENCIES) set(OpenCV_DEPENDS ${proj}) if(NOT DEFINED OpenCV_DIR) set(additional_cmake_args -DBUILD_opencv_java:BOOL=OFF -DBUILD_opencv_ts:BOOL=OFF -DBUILD_PERF_TESTS:BOOL=OFF -DBUILD_opencv_python:BOOL=OFF -DBUILD_opencv_python3:BOOL=OFF -DBUILD_opencv_python_bindings_generator:BOOL=OFF #-DBUILD_NEW_PYTHON_SUPPORT:BOOL=OFF ) if(MITK_USE_Qt5) list(APPEND additional_cmake_args -DWITH_QT:BOOL=OFF -DWITH_QT_OPENGL:BOOL=OFF -DQT_QMAKE_EXECUTABLE:FILEPATH=${QT_QMAKE_EXECUTABLE} ) endif() if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() mitk_query_custom_ep_vars() set(opencv_url ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/opencv-3.4.8.tar.gz) set(opencv_url_md5 5aa8240c28c00a7dacdf51698e0ced77) ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} URL ${opencv_url} URL_MD5 ${opencv_url_md5} CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} -DBUILD_TESTS:BOOL=OFF -DBUILD_DOCS:BOOL=OFF -DBUILD_EXAMPLES:BOOL=OFF -DBUILD_DOXYGEN_DOCS:BOOL=OFF -DWITH_CUDA:BOOL=OFF -DWITH_VTK:BOOL=OFF -DENABLE_CXX11:BOOL=ON -DWITH_IPP:BOOL=OFF -DBUILD_IPP_IW:BOOL=OFF + -DENABLE_PRECOMPILED_HEADERS:BOOL=OFF ${additional_cmake_args} ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(OpenCV_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() endif() diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkHistogramVisualizationWidget.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkHistogramVisualizationWidget.cpp index b7fabed2c2..9afc4b49d0 100644 --- a/Modules/ImageStatisticsUI/Qmitk/QmitkHistogramVisualizationWidget.cpp +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkHistogramVisualizationWidget.cpp @@ -1,194 +1,193 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkHistogramVisualizationWidget.h" #include QmitkHistogramVisualizationWidget::QmitkHistogramVisualizationWidget(QWidget* parent) : QWidget(parent) { m_Controls.setupUi(this); m_Controls.checkBoxShowSubchart->setChecked(false); m_Controls.spinBoxNBins->setValue(m_DefaultNBins); m_Controls.spinBoxNBins->setMinimum(m_MinNBins); m_Controls.spinBoxNBins->setMaximum(m_MaxNBins); SetGUIElementsEnabled(false); CreateConnections(); } void QmitkHistogramVisualizationWidget::SetHistogram(itk::Statistics::Histogram::ConstPointer histogram, const std::string& dataLabel) { if (histogram == nullptr) return; m_Histogram = histogram; m_Controls.chartWidget->AddData2D(ConvertHistogramToMap(m_Histogram), dataLabel); m_Controls.chartWidget->SetChartType(dataLabel, QmitkChartWidget::ChartType::bar); m_Controls.chartWidget->SetXAxisLabel("Gray value"); m_Controls.chartWidget->SetYAxisLabel("Frequency"); - m_Controls.chartWidget->SetShowLegend(false); m_Controls.chartWidget->Show(m_Controls.checkBoxShowSubchart->isChecked()); SetGUIElementsEnabled(true); } void QmitkHistogramVisualizationWidget::Reset() { m_Controls.chartWidget->Clear(); SetGUIElementsEnabled(false); } int QmitkHistogramVisualizationWidget::GetBins() { return m_Controls.spinBoxNBins->value(); } void QmitkHistogramVisualizationWidget::ResetDefault() { m_Controls.checkBoxUseDefaultNBins->setChecked(true); m_Controls.spinBoxNBins->setEnabled(false); m_Controls.spinBoxNBins->setValue(100); m_Controls.checkBoxShowSubchart->setChecked(false); } void QmitkHistogramVisualizationWidget::SetTheme(QmitkChartWidget::ColorTheme style) { m_Controls.chartWidget->SetTheme(style); } void QmitkHistogramVisualizationWidget::CreateConnections() { connect(m_Controls.buttonCopyHistogramToClipboard, &QPushButton::clicked, this, &QmitkHistogramVisualizationWidget::OnClipboardButtonClicked); connect(m_Controls.checkBoxUseDefaultNBins, &QCheckBox::clicked, this, &QmitkHistogramVisualizationWidget::OnDefaultNBinsCheckBoxChanged); connect(m_Controls.spinBoxNBins, &QSpinBox::editingFinished, this, &QmitkHistogramVisualizationWidget::OnNBinsSpinBoxValueChanged); connect(m_Controls.checkBoxShowSubchart, &QCheckBox::clicked, this, &QmitkHistogramVisualizationWidget::OnShowSubchartCheckBoxChanged); connect(m_Controls.checkBoxViewMinMax, &QCheckBox::clicked, this, &QmitkHistogramVisualizationWidget::OnViewMinMaxCheckBoxChanged); connect(m_Controls.doubleSpinBoxMaxValue, &QSpinBox::editingFinished, this, &QmitkHistogramVisualizationWidget::OnMaxValueSpinBoxValueChanged); connect(m_Controls.doubleSpinBoxMinValue, &QSpinBox::editingFinished, this, &QmitkHistogramVisualizationWidget::OnMinValueSpinBoxValueChanged); } void QmitkHistogramVisualizationWidget::SetGUIElementsEnabled(bool enabled) { this->setEnabled(enabled); m_Controls.tabWidgetPlot->setEnabled(enabled); m_Controls.checkBoxShowSubchart->setEnabled(enabled); m_Controls.checkBoxUseDefaultNBins->setEnabled(enabled); m_Controls.spinBoxNBins->setEnabled(!m_Controls.checkBoxUseDefaultNBins->isChecked()); m_Controls.buttonCopyHistogramToClipboard->setEnabled(enabled); m_Controls.checkBoxViewMinMax->setEnabled(enabled); m_Controls.doubleSpinBoxMaxValue->setEnabled(m_Controls.checkBoxViewMinMax->isChecked()); m_Controls.doubleSpinBoxMinValue->setEnabled(m_Controls.checkBoxViewMinMax->isChecked()); } std::map QmitkHistogramVisualizationWidget::ConvertHistogramToMap(itk::Statistics::Histogram::ConstPointer histogram) const { std::map histogramMap; if (histogram) { auto endIt = histogram->End(); auto it = histogram->Begin(); // generating Lists of measurement and frequencies for (; it != endIt; ++it) { double frequency = it.GetFrequency(); double measurement = it.GetMeasurementVector()[0]; histogramMap.emplace(measurement, frequency); } } return histogramMap; } void QmitkHistogramVisualizationWidget::OnClipboardButtonClicked() { if (m_Histogram) { QApplication::clipboard()->clear(); QString clipboard("Measurement \t Frequency\n"); auto iter = m_Histogram->Begin(); auto iterEnd = m_Histogram->End(); for (; iter != iterEnd; ++iter) { clipboard = clipboard.append("%L1 \t %L2\n") .arg(iter.GetMeasurementVector()[0], 0, 'f', 2) .arg(iter.GetFrequency()); } QApplication::clipboard()->setText(clipboard, QClipboard::Clipboard); } } void QmitkHistogramVisualizationWidget::OnDefaultNBinsCheckBoxChanged() { if (m_Controls.checkBoxUseDefaultNBins->isChecked()) { m_Controls.spinBoxNBins->setEnabled(false); if (m_Controls.spinBoxNBins->value() != static_cast(m_DefaultNBins) ) { m_Controls.spinBoxNBins->setValue(m_DefaultNBins); OnNBinsSpinBoxValueChanged(); } } else { m_Controls.spinBoxNBins->setEnabled(true); } } void QmitkHistogramVisualizationWidget::OnNBinsSpinBoxValueChanged() { emit RequestHistogramUpdate(m_Controls.spinBoxNBins->value()); } void QmitkHistogramVisualizationWidget::OnShowSubchartCheckBoxChanged() { m_Controls.chartWidget->Show(m_Controls.checkBoxShowSubchart->isChecked()); } void QmitkHistogramVisualizationWidget::OnViewMinMaxCheckBoxChanged() { double min = m_Histogram->GetBinMin(0, 0); auto maxVector = m_Histogram->GetDimensionMaxs(0); double max; if (m_Controls.checkBoxUseDefaultNBins->isChecked()) max = maxVector[m_DefaultNBins - 1]; else max = maxVector[m_Controls.spinBoxNBins->value() - 1]; if (!m_Controls.checkBoxViewMinMax->isChecked()) { m_Controls.doubleSpinBoxMaxValue->setEnabled(false); m_Controls.doubleSpinBoxMinValue->setEnabled(false); m_Controls.chartWidget->Reload(); } else { m_Controls.doubleSpinBoxMinValue->setMinimum(min); m_Controls.doubleSpinBoxMinValue->setValue(min); m_Controls.doubleSpinBoxMaxValue->setMaximum(max); m_Controls.doubleSpinBoxMaxValue->setValue(max); m_Controls.doubleSpinBoxMaxValue->setEnabled(true); m_Controls.doubleSpinBoxMinValue->setEnabled(true); } } void QmitkHistogramVisualizationWidget::OnMinValueSpinBoxValueChanged() { m_Controls.doubleSpinBoxMaxValue->setMinimum(m_Controls.doubleSpinBoxMinValue->value()+1); m_Controls.chartWidget->SetMinMaxValueXView(m_Controls.doubleSpinBoxMinValue->value(),m_Controls.doubleSpinBoxMaxValue->value()); m_Controls.chartWidget->Show(); } void QmitkHistogramVisualizationWidget::OnMaxValueSpinBoxValueChanged() { m_Controls.doubleSpinBoxMinValue->setMaximum(m_Controls.doubleSpinBoxMaxValue->value()-1); m_Controls.chartWidget->SetMinMaxValueXView(m_Controls.doubleSpinBoxMinValue->value(),m_Controls.doubleSpinBoxMaxValue->value()); m_Controls.chartWidget->Show(); } diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsCalculationRunnable.cpp b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsCalculationRunnable.cpp new file mode 100644 index 0000000000..69af24f548 --- /dev/null +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsCalculationRunnable.cpp @@ -0,0 +1,226 @@ +/*=================================================================== + +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 "QmitkImageStatisticsCalculationRunnable.h" + +#include "mitkImageStatisticsCalculator.h" +#include +#include +#include + +QmitkImageStatisticsCalculationRunnable::QmitkImageStatisticsCalculationRunnable() + : QObject(), QRunnable() + , m_StatisticsImage(nullptr) + , m_BinaryMask(nullptr) + , m_PlanarFigureMask(nullptr) + , m_IgnoreZeros(false) + , m_HistogramNBins(100) + , m_CalculationSuccessful(false) +{ +} + +QmitkImageStatisticsCalculationRunnable::~QmitkImageStatisticsCalculationRunnable() +{ +} + +void QmitkImageStatisticsCalculationRunnable::Initialize(const mitk::Image *image, + const mitk::Image *binaryImage, + const mitk::PlanarFigure *planarFig) +{ + this->m_StatisticsImage = image; + this->m_BinaryMask = binaryImage; + this->m_PlanarFigureMask = planarFig; +} + +mitk::ImageStatisticsContainer* QmitkImageStatisticsCalculationRunnable::GetStatisticsData() const +{ + return this->m_StatisticsContainer.GetPointer(); +} + +const mitk::Image* QmitkImageStatisticsCalculationRunnable::GetStatisticsImage() const +{ + return this->m_StatisticsImage.GetPointer(); +} + +const mitk::Image* QmitkImageStatisticsCalculationRunnable::GetMaskImage() const +{ + return this->m_BinaryMask.GetPointer(); +} + +const mitk::PlanarFigure* QmitkImageStatisticsCalculationRunnable::GetPlanarFigure() const +{ + return this->m_PlanarFigureMask.GetPointer(); +} + +void QmitkImageStatisticsCalculationRunnable::SetIgnoreZeroValueVoxel(bool _arg) +{ + this->m_IgnoreZeros = _arg; +} + +bool QmitkImageStatisticsCalculationRunnable::GetIgnoreZeroValueVoxel() const +{ + return this->m_IgnoreZeros; +} + +void QmitkImageStatisticsCalculationRunnable::SetHistogramNBins(unsigned int nbins) +{ + this->m_HistogramNBins = nbins; +} + +unsigned int QmitkImageStatisticsCalculationRunnable::GetHistogramNBins() const +{ + return this->m_HistogramNBins; +} + +std::string QmitkImageStatisticsCalculationRunnable::GetLastErrorMessage() const +{ + return m_message; +} + +const QmitkImageStatisticsCalculationRunnable::HistogramType* +QmitkImageStatisticsCalculationRunnable::GetTimeStepHistogram(unsigned int t) const +{ + if (t >= this->m_HistogramVector.size()) + return nullptr; + + return this->m_HistogramVector.at(t).GetPointer(); +} + +bool QmitkImageStatisticsCalculationRunnable::GetStatisticsUpdateSuccessFlag() const +{ + return m_CalculationSuccessful; +} + +void QmitkImageStatisticsCalculationRunnable::run() +{ + bool statisticCalculationSuccessful = true; + mitk::ImageStatisticsCalculator::Pointer calculator = mitk::ImageStatisticsCalculator::New(); + + if (this->m_StatisticsImage.IsNotNull()) + { + calculator->SetInputImage(m_StatisticsImage); + } + else + { + statisticCalculationSuccessful = false; + } + + // Bug 13416 : The ImageStatistics::SetImageMask() method can throw exceptions, i.e. when the dimensionality + // of the masked and input image differ, we need to catch them and mark the calculation as failed + // the same holds for the ::SetPlanarFigure() + try + { + if (this->m_BinaryMask.IsNotNull()) + { + mitk::ImageMaskGenerator::Pointer imgMask = mitk::ImageMaskGenerator::New(); + imgMask->SetImageMask(m_BinaryMask->Clone()); + calculator->SetMask(imgMask.GetPointer()); + } + if (this->m_PlanarFigureMask.IsNotNull()) + { + mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New(); + pfMaskGen->SetInputImage(m_StatisticsImage); + pfMaskGen->SetPlanarFigure(m_PlanarFigureMask->Clone()); + calculator->SetMask(pfMaskGen.GetPointer()); + } + } + catch (const mitk::Exception& e) + { + MITK_ERROR << "MITK Exception: " << e.what(); + m_message = e.what(); + statisticCalculationSuccessful = false; + } + catch (const itk::ExceptionObject& e) + { + MITK_ERROR << "ITK Exception:" << e.what(); + m_message = e.what(); + statisticCalculationSuccessful = false; + } + catch (const std::runtime_error &e) + { + MITK_ERROR << "Runtime Exception: " << e.what(); + m_message = e.what(); + statisticCalculationSuccessful = false; + } + catch (const std::exception &e) + { + MITK_ERROR << "Standard Exception: " << e.what(); + m_message = e.what(); + statisticCalculationSuccessful = false; + } + + if (this->m_IgnoreZeros) + { + mitk::IgnorePixelMaskGenerator::Pointer ignorePixelValueMaskGen = mitk::IgnorePixelMaskGenerator::New(); + ignorePixelValueMaskGen->SetIgnoredPixelValue(0); + ignorePixelValueMaskGen->SetInputImage(m_StatisticsImage); + calculator->SetSecondaryMask(ignorePixelValueMaskGen.GetPointer()); + } + else + { + calculator->SetSecondaryMask(nullptr); + } + + calculator->SetNBinsForHistogramStatistics(m_HistogramNBins); + + try + { + calculator->GetStatistics(); + } + catch (mitk::Exception& e) + { + m_message = e.GetDescription(); + MITK_ERROR << "MITK Exception: " << e.what(); + statisticCalculationSuccessful = false; + } + catch (const std::runtime_error &e) + { + m_message = "Failure: " + std::string(e.what()); + MITK_ERROR << "Runtime Exception: " << e.what(); + statisticCalculationSuccessful = false; + } + catch (const std::exception &e) + { + m_message = "Failure: " + std::string(e.what()); + MITK_ERROR << "Standard Exception: " << e.what(); + statisticCalculationSuccessful = false; + } + + this->m_CalculationSuccessful = statisticCalculationSuccessful; + + if (statisticCalculationSuccessful) + { + m_StatisticsContainer = calculator->GetStatistics(); + this->m_HistogramVector.clear(); + + for (unsigned int i = 0; i < m_StatisticsImage->GetTimeSteps(); i++) + { + HistogramType::ConstPointer tempHistogram; + try { + if (calculator->GetStatistics()->TimeStepExists(i)) + { + tempHistogram = calculator->GetStatistics()->GetStatisticsForTimeStep(i).m_Histogram; + this->m_HistogramVector.push_back(tempHistogram); + } + } + catch (mitk::Exception&) { + MITK_WARN << ":-("; + } + + } + } + emit finished(); +} diff --git a/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsCalculationRunnable.h b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsCalculationRunnable.h new file mode 100644 index 0000000000..d46d8e28c9 --- /dev/null +++ b/Modules/ImageStatisticsUI/Qmitk/QmitkImageStatisticsCalculationRunnable.h @@ -0,0 +1,101 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ +#ifndef QMITKIMAGESTATISTICSCALCULATIONRUNNABLE_H_INCLUDED +#define QMITKIMAGESTATISTICSCALCULATIONRUNNABLE_H_INCLUDED + +//QT headers +#include +#include +#include + +//mitk headers +#include "mitkImage.h" +#include "mitkPlanarFigure.h" +#include "mitkImageStatisticsContainer.h" +#include + +// itk headers +#ifndef __itkHistogram_h +#include +#endif + + +/** +* /brief This class is executed as background thread for image statistics calculation. +* +* This class is derived from QRunnable and is intended to be used by QmitkImageStatisticsView +* to run the image statistics calculation in a background thread keeping the GUI usable. +*/ +class MITKIMAGESTATISTICSUI_EXPORT QmitkImageStatisticsCalculationRunnable : public QObject, public QRunnable +{ + Q_OBJECT +public: + + typedef itk::Statistics::Histogram HistogramType; + + /*! + /brief standard constructor. */ + QmitkImageStatisticsCalculationRunnable(); + /*! + /brief standard destructor. */ + ~QmitkImageStatisticsCalculationRunnable(); + + /*! + /brief Initializes the object with necessary data. */ + void Initialize(const mitk::Image* image, const mitk::Image* binaryImage, const mitk::PlanarFigure* planarFig); + /*! + /brief returns the calculated image statistics. */ + mitk::ImageStatisticsContainer* GetStatisticsData() const; + + const mitk::Image* GetStatisticsImage() const; + const mitk::Image* GetMaskImage() const; + const mitk::PlanarFigure* GetPlanarFigure() const; + + /*! + /brief Set flag to ignore zero valued voxels */ + void SetIgnoreZeroValueVoxel(bool _arg); + /*! + /brief Get status of zero value voxel ignoring. */ + bool GetIgnoreZeroValueVoxel() const; + /*! + /brief Set bin size for histogram resolution.*/ + void SetHistogramNBins(unsigned int nbins); + /*! + /brief Get bin size for histogram resolution.*/ + unsigned int GetHistogramNBins() const; + /*! + /brief Returns the histogram of the currently selected time step. */ + const HistogramType* GetTimeStepHistogram(unsigned int t = 0) const; + + /*! + /brief Returns a flag the indicates if the statistics are updated successfully */ + bool GetStatisticsUpdateSuccessFlag() const; + /*! + /brief Method called once the thread is executed. */ + void run() override; + + std::string GetLastErrorMessage() const; +signals: + void finished(); + +private: + mitk::Image::ConstPointer m_StatisticsImage; ///< member variable holds the input image for which the statistics need to be calculated. + mitk::Image::ConstPointer m_BinaryMask; ///< member variable holds the binary mask image for segmentation image statistics calculation. + mitk::PlanarFigure::ConstPointer m_PlanarFigureMask; ///< member variable holds the planar figure for segmentation image statistics calculation. + mitk::ImageStatisticsContainer::Pointer m_StatisticsContainer; + bool m_IgnoreZeros; ///< member variable holds flag to indicate if zero valued voxel should be suppressed + unsigned int m_HistogramNBins; ///< member variable holds the bin size for histogram resolution. + bool m_CalculationSuccessful; ///< flag set if statistics calculation was successful + std::vector m_HistogramVector; ///< member holds the histograms of all time steps. + std::string m_message; +}; +#endif // QMITKIMAGESTATISTICSCALCULATIONRUNNABLE_H_INCLUDED diff --git a/Modules/ImageStatisticsUI/files.cmake b/Modules/ImageStatisticsUI/files.cmake index ef1f9c422c..d94da90986 100644 --- a/Modules/ImageStatisticsUI/files.cmake +++ b/Modules/ImageStatisticsUI/files.cmake @@ -1,31 +1,33 @@ set(CPP_FILES Qmitk/QmitkHistogramVisualizationWidget.cpp + Qmitk/QmitkImageStatisticsCalculationRunnable.cpp Qmitk/QmitkIntensityProfileVisualizationWidget.cpp Qmitk/QmitkImageStatisticsTreeModel.cpp Qmitk/QmitkImageStatisticsCalculationJob.cpp Qmitk/QmitkStatisticsModelToStringConverter.cpp Qmitk/QmitkImageStatisticsWidget.cpp Qmitk/QmitkImageStatisticsTreeItem.cpp ) set(H_FILES Qmitk/QmitkStatisticsModelToStringConverter.h Qmitk/QmitkImageStatisticsTreeItem.h ) set(TPP_FILES ) set(UI_FILES Qmitk/QmitkHistogramVisualizationWidget.ui Qmitk/QmitkIntensityProfileVisualizationWidget.ui Qmitk/QmitkImageStatisticsWidget.ui ) set(MOC_H_FILES Qmitk/QmitkHistogramVisualizationWidget.h + Qmitk/QmitkImageStatisticsCalculationRunnable.h Qmitk/QmitkIntensityProfileVisualizationWidget.h Qmitk/QmitkImageStatisticsTreeModel.h Qmitk/QmitkImageStatisticsCalculationJob.h Qmitk/QmitkImageStatisticsWidget.h ) diff --git a/Modules/MatchPointRegistration/Helper/mitkMaskedAlgorithmHelper.cpp b/Modules/MatchPointRegistration/Helper/mitkMaskedAlgorithmHelper.cpp index 97736cc969..f38021161a 100644 --- a/Modules/MatchPointRegistration/Helper/mitkMaskedAlgorithmHelper.cpp +++ b/Modules/MatchPointRegistration/Helper/mitkMaskedAlgorithmHelper.cpp @@ -1,183 +1,175 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkMaskedAlgorithmHelper.h" #include // Mitk #include // MatchPoint #include "mapMaskedRegistrationAlgorithmInterface.h" #include namespace mitk { MaskedAlgorithmHelper::MaskedAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase* algorithm) : m_AlgorithmBase(algorithm) { } bool MaskedAlgorithmHelper::HasMaskedRegistrationAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm) { using MaskedInterface2D = const ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<2, 2>; using MaskedInterface3D = const ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<3, 3>; return dynamic_cast(algorithm) != nullptr && dynamic_cast(algorithm) != nullptr; }; bool MaskedAlgorithmHelper:: CheckSupport(const mitk::Image* movingMask, const mitk::Image* targetMask) const { if (! m_AlgorithmBase) mapDefaultExceptionStaticMacro(<< "Error, cannot check data. Helper has no algorithm defined."); unsigned int movingDim = m_AlgorithmBase->getMovingDimensions(); unsigned int targetDim = m_AlgorithmBase->getTargetDimensions(); bool result = movingDim == targetDim; if ( movingMask) { result = result && (movingMask->GetDimension() == movingDim); - - if (movingDim == 2) - { - typedef itk::Image MaskImageType; - mitk::PixelType maskPixelType = mitk::MakePixelType(); - - result = result && (maskPixelType == movingMask->GetPixelType()); - } - else if (movingDim == 3) - { - typedef itk::Image MaskImageType; - mitk::PixelType maskPixelType = mitk::MakePixelType(); - - result = result && (maskPixelType == movingMask->GetPixelType()); - } } if ( targetMask) { result = result && (targetMask->GetDimension() == targetDim); - - if (movingDim == 2) - { - typedef itk::Image MaskImageType; - mitk::PixelType maskPixelType = mitk::MakePixelType(); - - result = result && (maskPixelType == targetMask->GetPixelType()); - } - else if (movingDim == 3) - { - typedef itk::Image MaskImageType; - mitk::PixelType maskPixelType = mitk::MakePixelType(); - - result = result && (maskPixelType == targetMask->GetPixelType()); - } } if (movingDim == 2) { typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<2, 2> MaskedInterface; const MaskedInterface* pMaskedReg = dynamic_cast(m_AlgorithmBase.GetPointer()); result = result && pMaskedReg; } else if (movingDim == 3) { typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface<3, 3> MaskedInterface; const MaskedInterface* pMaskedReg = dynamic_cast(m_AlgorithmBase.GetPointer()); result = result && pMaskedReg; } else { result = false; } return result; }; bool MaskedAlgorithmHelper::SetMasks(const mitk::Image* movingMask, const mitk::Image* targetMask) { if (! m_AlgorithmBase) mapDefaultExceptionStaticMacro(<< "Error, cannot set data. Helper has no algorithm defined."); if (! CheckSupport(movingMask, targetMask)) return false; unsigned int movingDim = m_AlgorithmBase->getMovingDimensions(); unsigned int targetDim = m_AlgorithmBase->getTargetDimensions(); if (movingDim!=targetDim) return false; if (movingDim == 2) { return DoSetMasks<2,2>(movingMask, targetMask); } else if (movingDim == 3) { return DoSetMasks<3,3>(movingMask, targetMask); } return false; }; - template + template bool MaskedAlgorithmHelper::DoSetMasks(const mitk::Image* movingMask, const mitk::Image* targetMask) { - typedef itk::SpatialObject MovingSpatialType; - typedef itk::SpatialObject TargetSpatialType; + typedef itk::SpatialObject MovingSpatialType; + typedef itk::SpatialObject TargetSpatialType; - typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface MaskedRegInterface; + typedef ::map::algorithm::facet::MaskedRegistrationAlgorithmInterface MaskedRegInterface; MaskedRegInterface* pAlg = dynamic_cast(m_AlgorithmBase.GetPointer()); if (!pAlg) return false; if (movingMask) { - AccessFixedTypeByItk(movingMask, DoConvertMask, (MaskPixelType), (VImageDimension1)); + AccessFixedDimensionByItk(movingMask, DoConvertMask, VMovingDimension); typename MovingSpatialType::Pointer movingSpatial = dynamic_cast(m_convertResult.GetPointer()); - if (! movingSpatial) mapDefaultExceptionStaticMacro(<< "Error, cannot convert moving mask."); + if (!movingSpatial) mapDefaultExceptionStaticMacro(<< "Error, cannot convert moving mask."); pAlg->setMovingMask(movingSpatial); } else { pAlg->setMovingMask(nullptr); } if (targetMask) { - AccessFixedTypeByItk(targetMask, DoConvertMask, (MaskPixelType), (VImageDimension2)); + AccessFixedDimensionByItk(targetMask, DoConvertMask, VTargetDimension); typename TargetSpatialType::Pointer targetSpatial = dynamic_cast(m_convertResult.GetPointer()); if (! targetSpatial) mapDefaultExceptionStaticMacro(<< "Error, cannot convert moving mask."); pAlg->setTargetMask(targetSpatial); } else { pAlg->setTargetMask(nullptr); } return true; } - template - void MaskedAlgorithmHelper::DoConvertMask(const itk::Image* mask) + template + typename itk::SpatialObject::Pointer + MaskedAlgorithmHelper::ConvertMaskSO(const itk::Image* mask) const { typedef itk::ImageMaskSpatialObject SpatialType; typename SpatialType::Pointer spatial = SpatialType::New(); spatial->SetImage(mask); - m_convertResult = spatial.GetPointer(); + return spatial.GetPointer(); + } + + template + void MaskedAlgorithmHelper::DoConvertMask(const itk::Image* mask) + { + using InImageType = itk::Image; + using MaskImageType = itk::Image; + + typedef itk::CastImageFilter< InImageType, MaskImageType > CastFilterType; + typename CastFilterType::Pointer imageCaster = CastFilterType::New(); + + imageCaster->SetInput(mask); + + auto castedMask = imageCaster->GetOutput(); + imageCaster->Update(); + m_convertResult = ConvertMaskSO(castedMask); } + template + void MaskedAlgorithmHelper::DoConvertMask(const itk::Image* mask) + { + m_convertResult = ConvertMaskSO(mask); + } } diff --git a/Modules/MatchPointRegistration/Helper/mitkMaskedAlgorithmHelper.h b/Modules/MatchPointRegistration/Helper/mitkMaskedAlgorithmHelper.h index 515ebc90c8..4d12409887 100644 --- a/Modules/MatchPointRegistration/Helper/mitkMaskedAlgorithmHelper.h +++ b/Modules/MatchPointRegistration/Helper/mitkMaskedAlgorithmHelper.h @@ -1,78 +1,86 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkMaskedAlgorithmHelper_h #define mitkMaskedAlgorithmHelper_h +#include "itkSpatialObject.h" //MatchPoint #include "mapRegistrationAlgorithmBase.h" //MITK #include //MITK #include "MitkMatchPointRegistrationExports.h" namespace mitk { /*! \brief MaskedAlgorithmHelper Helper class as an easy bridge to set mitk images as masks for registration algorithms. It is assumed that the Image indicates the mask by pixel values != 0. \remark Currently only 2D-2D and 3D-3D algorithms are supported. - \remark Currently only masks with pixel type unsigned char (default mitk segmentation images) are supported. \remark Current implementation is not thread-save. Just use one Helper class per registration task. */ class MITKMATCHPOINTREGISTRATION_EXPORT MaskedAlgorithmHelper { public: MaskedAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase* algorithm); /** Set one or both masks to an algorithm. * If the algorithm does not support masks it will be ignored. * @remark Set a mask to nullptr if you don't want to set it. * @return Indicates if the masks could be set/was supported by algorithm.*/ bool SetMasks(const mitk::Image* movingMask, const mitk::Image* targetMask); /** Checks if the algorithm supports masks of the passed type.*/ bool CheckSupport(const mitk::Image* movingMask, const mitk::Image* targetMask) const; static bool HasMaskedRegistrationAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm); ~MaskedAlgorithmHelper() {} private: - typedef unsigned char MaskPixelType; + using MaskPixelType = unsigned char; MaskedAlgorithmHelper& operator = (const MaskedAlgorithmHelper&); MaskedAlgorithmHelper(const MaskedAlgorithmHelper&); /**Internal helper that is used by SetMasks if the data are images to set them properly.*/ template bool DoSetMasks(const mitk::Image* movingMask, const mitk::Image* targetMask); - /**Internal helper that is used by SetData if the data are images to set them properly.*/ + /**Internal helper that is used by SetData if the data are images to cast and set them properly.*/ template void DoConvertMask(const itk::Image* mask); + /**Internal helper that is used by SetData if the data are images to set them properly.*/ + template + void DoConvertMask(const itk::Image* mask); + + /**Internal helper that is used to pack the mask image into a spatial object.*/ + template + typename itk::SpatialObject::Pointer ConvertMaskSO(const itk::Image* mask) const; + /**Helper member that containes the result of the last call of DoConvertMask().*/ itk::DataObject::Pointer m_convertResult; map::algorithm::RegistrationAlgorithmBase::Pointer m_AlgorithmBase; }; } #endif diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index c441d79792..ebc637fbf1 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -1,628 +1,593 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkImageStatisticsView.h" #include // berry includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include #include "mitkImageStatisticsContainerManager.h" #include const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics"; -QmitkImageStatisticsView::QmitkImageStatisticsView(QObject * /*parent*/, const char * /*name*/) -{ - this->m_CalculationJob = new QmitkImageStatisticsCalculationJob(); -} - QmitkImageStatisticsView::~QmitkImageStatisticsView() { - if (m_selectedPlanarFigure) + if (nullptr != m_selectedPlanarFigure) { m_selectedPlanarFigure->RemoveObserver(m_PlanarFigureObserverTag); } - - if (!m_CalculationJob->isFinished()) - { - m_CalculationJob->terminate(); - m_CalculationJob->wait(); - } - this->m_CalculationJob->deleteLater(); } void QmitkImageStatisticsView::CreateQtPartControl(QWidget *parent) { m_Controls.setupUi(parent); - m_Controls.widget_histogram->SetTheme(this->GetColorTheme()); - m_Controls.widget_intensityProfile->SetTheme(this->GetColorTheme()); + m_Controls.widget_histogram->SetTheme(GetColorTheme()); + m_Controls.widget_intensityProfile->SetTheme(GetColorTheme()); m_Controls.groupBox_histogram->setVisible(true); m_Controls.groupBox_intensityProfile->setVisible(false); m_Controls.label_currentlyComputingStatistics->setVisible(false); m_Controls.sliderWidget_histogram->setPrefix("Time: "); m_Controls.sliderWidget_histogram->setDecimals(0); m_Controls.sliderWidget_histogram->setVisible(false); m_Controls.sliderWidget_intensityProfile->setPrefix("Time: "); m_Controls.sliderWidget_intensityProfile->setDecimals(0); m_Controls.sliderWidget_intensityProfile->setVisible(false); ResetGUI(); - PrepareDataStorageComboBoxes(); - m_Controls.widget_statistics->SetDataStorage(this->GetDataStorage()); + m_Controls.widget_statistics->SetDataStorage(GetDataStorage()); CreateConnections(); } void QmitkImageStatisticsView::CreateConnections() { - connect(this->m_CalculationJob, - &QmitkImageStatisticsCalculationJob::finished, - this, - &QmitkImageStatisticsView::OnStatisticsCalculationEnds, - Qt::QueuedConnection); - connect(this->m_Controls.checkBox_ignoreZero, - &QCheckBox::stateChanged, - this, - &QmitkImageStatisticsView::OnCheckBoxIgnoreZeroStateChanged); - connect(this->m_Controls.sliderWidget_histogram, - &ctkSliderWidget::valueChanged, - this, - &QmitkImageStatisticsView::OnSliderWidgetHistogramChanged); - connect(this->m_Controls.sliderWidget_intensityProfile, - &ctkSliderWidget::valueChanged, - this, - &QmitkImageStatisticsView::OnSliderWidgetIntensityProfileChanged); - connect(this->m_Controls.imageSelector, - static_cast(&QComboBox::currentIndexChanged), - this, - &QmitkImageStatisticsView::OnImageSelectorChanged); - connect(this->m_Controls.maskImageSelector, - static_cast(&QComboBox::currentIndexChanged), - this, - &QmitkImageStatisticsView::OnMaskSelectorChanged); -} - -void QmitkImageStatisticsView::OnCheckBoxIgnoreZeroStateChanged(int state) -{ - m_ForceRecompute = true; - if (state != Qt::Unchecked) - { - this->m_CalculationJob->SetIgnoreZeroValueVoxel(true); - } - else - { - this->m_CalculationJob->SetIgnoreZeroValueVoxel(false); - } - CalculateOrGetStatistics(); + connect(m_Controls.checkBox_ignoreZero, &QCheckBox::stateChanged, + this, &QmitkImageStatisticsView::OnCheckBoxIgnoreZeroStateChanged); + connect(m_Controls.buttonSelection, &QAbstractButton::clicked, + this, &QmitkImageStatisticsView::OnButtonSelectionPressed); } -void QmitkImageStatisticsView::OnSliderWidgetHistogramChanged(double value) +void QmitkImageStatisticsView::CalculateOrGetMultiStatistics() { - unsigned int timeStep = static_cast(value); - auto mask = m_selectedMaskNode ? m_selectedMaskNode->GetData() : nullptr; - auto imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics( - this->GetDataStorage(), m_selectedImageNode->GetData(), mask); - HistogramType::ConstPointer histogram = nullptr; - if (imageStatistics->TimeStepExists(timeStep)) - { - histogram = imageStatistics->GetStatisticsForTimeStep(timeStep).m_Histogram; - } + m_selectedImageNode = nullptr; + m_selectedMaskNode = nullptr; - if (this->m_CalculationJob->GetStatisticsUpdateSuccessFlag()) + if (m_selectedImageNodes.empty()) { - if (histogram.IsNotNull()) - { - this->FillHistogramWidget({histogram}, {m_selectedImageNode->GetName()}); - } - else - { - HistogramType::Pointer emptyHistogram = HistogramType::New(); - this->FillHistogramWidget({emptyHistogram}, {m_selectedImageNode->GetName()}); - } + // no images selected; reset everything + ResetGUI(); } -} - -void QmitkImageStatisticsView::OnSliderWidgetIntensityProfileChanged() -{ - // intensity profile is always computed on request, not stored as node in DataStorage - auto image = dynamic_cast(m_selectedImageNode->GetData()); - auto planarFigure = dynamic_cast(m_selectedMaskNode->GetData()); - if (image && planarFigure && this->m_CalculationJob->GetStatisticsUpdateSuccessFlag()) + for (auto imageNode : m_selectedImageNodes) { - this->ComputeAndDisplayIntensityProfile(image, planarFigure); - } -} - -void QmitkImageStatisticsView::PartClosed(const berry::IWorkbenchPartReference::Pointer &) {} + m_selectedImageNode = imageNode; -void QmitkImageStatisticsView::FillHistogramWidget(const std::vector &histogram, - const std::vector &dataLabels) -{ - m_Controls.groupBox_histogram->setVisible(true); - m_Controls.widget_histogram->SetTheme(this->GetColorTheme()); - m_Controls.widget_histogram->Reset(); - m_Controls.widget_histogram->SetHistogram(histogram.front(), dataLabels.front()); - connect(m_Controls.widget_histogram, - &QmitkHistogramVisualizationWidget::RequestHistogramUpdate, - this, - &QmitkImageStatisticsView::OnRequestHistogramUpdate); -} - -QmitkChartWidget::ColorTheme QmitkImageStatisticsView::GetColorTheme() const -{ - ctkPluginContext *context = berry::WorkbenchPlugin::GetDefault()->GetPluginContext(); - ctkServiceReference styleManagerRef = context->getServiceReference(); - if (styleManagerRef) - { - auto styleManager = context->getService(styleManagerRef); - if (styleManager->GetStyle().name == "Dark") + if (m_selectedMaskNodes.empty()) { - return QmitkChartWidget::ColorTheme::darkstyle; - } - else - { - return QmitkChartWidget::ColorTheme::lightstyle; - } - } - return QmitkChartWidget::ColorTheme::darkstyle; -} - -void QmitkImageStatisticsView::OnImageSelectorChanged() -{ - auto selectedImageNode = m_Controls.imageSelector->GetSelectedNode(); - if (selectedImageNode != m_selectedImageNode) - { - m_selectedImageNode = selectedImageNode; - if (m_selectedImageNode.IsNotNull()) - { - ResetGUIDefault(); - - auto isPlanarFigurePredicate = mitk::GetImageStatisticsPlanarFigurePredicate(); - auto isMaskPredicate = mitk::GetImageStatisticsMaskPredicate(); - auto hasSameGeometry = mitk::NodePredicateGeometry::New(m_selectedImageNode->GetData()->GetGeometry()); - hasSameGeometry->SetCheckPrecision(1e-10); - auto isMaskWithGeometryPredicate = mitk::NodePredicateAnd::New(isMaskPredicate, hasSameGeometry); - auto isMaskOrPlanarFigureWithGeometryPredicate = - mitk::NodePredicateOr::New(isPlanarFigurePredicate, isMaskWithGeometryPredicate); - // prevent triggering of computation as the predicate triggers a signalChanged event - m_Controls.maskImageSelector->disconnect(); - m_Controls.maskImageSelector->SetPredicate(isMaskOrPlanarFigureWithGeometryPredicate); - // reset mask to - m_Controls.maskImageSelector->SetZeroEntryText(""); - m_Controls.checkBox_ignoreZero->setEnabled(true); - m_selectedMaskNode = nullptr; - m_Controls.widget_statistics->SetMaskNodes({}); CalculateOrGetStatistics(); - m_Controls.widget_statistics->SetImageNodes({m_selectedImageNode}); - connect(this->m_Controls.maskImageSelector, - static_cast(&QComboBox::currentIndexChanged), - this, - &QmitkImageStatisticsView::OnMaskSelectorChanged); } else { - m_Controls.widget_statistics->SetImageNodes({}); - m_Controls.widget_statistics->SetMaskNodes({}); - m_Controls.widget_statistics->Reset(); - m_Controls.widget_histogram->Reset(); - ResetGUI(); - } - } -} - -void QmitkImageStatisticsView::OnMaskSelectorChanged() -{ - auto selectedMaskNode = m_Controls.maskImageSelector->GetSelectedNode(); - if (selectedMaskNode != m_selectedMaskNode) - { - m_selectedMaskNode = selectedMaskNode; - if (m_selectedMaskNode.IsNotNull()) - { - m_Controls.widget_statistics->SetMaskNodes({m_selectedMaskNode}); - } - else - { - m_Controls.widget_statistics->SetMaskNodes({}); + for (auto maskNode : m_selectedMaskNodes) + { + m_selectedMaskNode = maskNode; + CalculateOrGetStatistics(); + } } - CalculateOrGetStatistics(); } } void QmitkImageStatisticsView::CalculateOrGetStatistics() { - if (this->m_selectedPlanarFigure) + if (nullptr != m_selectedPlanarFigure) { - this->m_selectedPlanarFigure->RemoveObserver(this->m_PlanarFigureObserverTag); - this->m_selectedPlanarFigure = nullptr; + m_selectedPlanarFigure->RemoveObserver(m_PlanarFigureObserverTag); + m_selectedPlanarFigure = nullptr; } m_Controls.groupBox_intensityProfile->setVisible(false); m_Controls.widget_statistics->setEnabled(m_selectedImageNode.IsNotNull()); - if (m_selectedImageNode != nullptr) + if (nullptr != m_selectedImageNode) { - auto image = dynamic_cast(m_selectedImageNode->GetData()); + auto image = dynamic_cast(m_selectedImageNode->GetData()); mitk::Image *mask = nullptr; mitk::PlanarFigure *maskPlanarFigure = nullptr; if (image->GetDimension() == 4) { m_Controls.sliderWidget_histogram->setVisible(true); unsigned int maxTimestep = image->GetTimeSteps(); m_Controls.sliderWidget_histogram->setMaximum(maxTimestep - 1); } else { m_Controls.sliderWidget_histogram->setVisible(false); } - if (m_selectedMaskNode != nullptr) + if (nullptr != m_selectedMaskNode) { - mask = dynamic_cast(m_selectedMaskNode->GetData()); - if (mask == nullptr) + mask = dynamic_cast(m_selectedMaskNode->GetData()); + if (nullptr == mask) { maskPlanarFigure = dynamic_cast(m_selectedMaskNode->GetData()); } } mitk::ImageStatisticsContainer::ConstPointer imageStatistics; - if (mask) + if (nullptr != mask) { - imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(this->GetDataStorage(), image, mask); + imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image, mask); } - else if (maskPlanarFigure) + else if (nullptr != maskPlanarFigure) { m_selectedPlanarFigure = maskPlanarFigure; ITKCommandType::Pointer changeListener = ITKCommandType::New(); - changeListener->SetCallbackFunction(this, &QmitkImageStatisticsView::CalculateOrGetStatistics); - this->m_PlanarFigureObserverTag = + changeListener->SetCallbackFunction(this, &QmitkImageStatisticsView::CalculateOrGetMultiStatistics); + m_PlanarFigureObserverTag = m_selectedPlanarFigure->AddObserver(mitk::EndInteractionPlanarFigureEvent(), changeListener); if (!maskPlanarFigure->IsClosed()) { ComputeAndDisplayIntensityProfile(image, maskPlanarFigure); } imageStatistics = - mitk::ImageStatisticsContainerManager::GetImageStatistics(this->GetDataStorage(), image, maskPlanarFigure); + mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image, maskPlanarFigure); } else { - imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(this->GetDataStorage(), image); + imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image); } bool imageStatisticsOlderThanInputs = false; - if (imageStatistics && - (imageStatistics->GetMTime() < image->GetMTime() || (mask && imageStatistics->GetMTime() < mask->GetMTime()) || - (maskPlanarFigure && imageStatistics->GetMTime() < maskPlanarFigure->GetMTime()))) + if (nullptr != imageStatistics && + ((nullptr != image && imageStatistics->GetMTime() < image->GetMTime()) || + (nullptr != mask && imageStatistics->GetMTime() < mask->GetMTime()) || + (nullptr != maskPlanarFigure && imageStatistics->GetMTime() < maskPlanarFigure->GetMTime()))) { imageStatisticsOlderThanInputs = true; } - if (imageStatistics) + if (nullptr != imageStatistics) { - // triggers recomputation when switched between images and the newest one has not 100 bins (default) - auto calculatedBins = imageStatistics->GetStatisticsForTimeStep(0).m_Histogram.GetPointer()->Size(); - if (calculatedBins != 100) + // triggers re-computation when switched between images and the newest one has not 100 bins (default) + if (imageStatistics->TimeStepExists(0)) { - OnRequestHistogramUpdate(m_Controls.widget_histogram->GetBins()); + auto calculatedBins = imageStatistics->GetStatisticsForTimeStep(0).m_Histogram.GetPointer()->Size(); + if (calculatedBins != 100) + { + OnRequestHistogramUpdate(m_Controls.widget_histogram->GetBins()); + } } } // statistics need to be computed if (!imageStatistics || imageStatisticsOlderThanInputs || m_ForceRecompute) { CalculateStatistics(image, mask, maskPlanarFigure); } // statistics already computed else { // Not an open planar figure: show histogram (intensity profile already shown) if (!(maskPlanarFigure && !maskPlanarFigure->IsClosed())) { if (imageStatistics->TimeStepExists(0)) { auto histogram = imageStatistics->GetStatisticsForTimeStep(0).m_Histogram.GetPointer(); - std::string imageNodeName = m_selectedImageNode->GetName(); - this->FillHistogramWidget({histogram}, {imageNodeName}); + auto histogramLabelName = GenerateStatisticsNodeName(image, mask); + FillHistogramWidget({ histogram }, { histogramLabelName }); } } } } - else +} + +void QmitkImageStatisticsView::CalculateStatistics(const mitk::Image *image, const mitk::Image *mask, + const mitk::PlanarFigure *maskPlanarFigure) +{ + auto runnable = new QmitkImageStatisticsCalculationRunnable(); + runnable->Initialize(image, mask, maskPlanarFigure); + runnable->setAutoDelete(false); + runnable->SetIgnoreZeroValueVoxel(m_IgnoreZeroValueVoxel); + m_Runnables.push_back(runnable); + connect(runnable, &QmitkImageStatisticsCalculationRunnable::finished, + this, &QmitkImageStatisticsView::OnStatisticsCalculationEnds, Qt::QueuedConnection); + + try { - ResetGUI(); + // Compute statistics + QThreadPool::globalInstance()->start(runnable); + m_Controls.label_currentlyComputingStatistics->setVisible(true); + } + catch (const mitk::Exception &e) + { + mitk::StatusBar::GetInstance()->DisplayErrorText(e.GetDescription()); + m_Controls.label_currentlyComputingStatistics->setVisible(false); + } + catch (const std::runtime_error &e) + { + mitk::StatusBar::GetInstance()->DisplayErrorText(e.what()); + m_Controls.label_currentlyComputingStatistics->setVisible(false); + } + catch (const std::exception &e) + { + mitk::StatusBar::GetInstance()->DisplayErrorText(e.what()); + m_Controls.label_currentlyComputingStatistics->setVisible(false); } - m_ForceRecompute = false; } void QmitkImageStatisticsView::ComputeAndDisplayIntensityProfile(mitk::Image *image, mitk::PlanarFigure *maskPlanarFigure) { mitk::Image::Pointer inputImage; if (image->GetDimension() == 4) { m_Controls.sliderWidget_intensityProfile->setVisible(true); unsigned int maxTimestep = image->GetTimeSteps(); m_Controls.sliderWidget_intensityProfile->setMaximum(maxTimestep - 1); // Intensity profile can only be calculated on 3D, so extract if 4D mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); int currentTimestep = static_cast(m_Controls.sliderWidget_intensityProfile->value()); timeSelector->SetInput(image); timeSelector->SetTimeNr(currentTimestep); timeSelector->Update(); inputImage = timeSelector->GetOutput(); } else { m_Controls.sliderWidget_intensityProfile->setVisible(false); inputImage = image; } auto intensityProfile = mitk::ComputeIntensityProfile(inputImage, maskPlanarFigure); // Don't show histogram for intensity profiles m_Controls.groupBox_histogram->setVisible(false); m_Controls.groupBox_intensityProfile->setVisible(true); m_Controls.widget_intensityProfile->Reset(); m_Controls.widget_intensityProfile->SetIntensityProfile(intensityProfile.GetPointer(), "Intensity Profile of " + m_selectedImageNode->GetName()); } +void QmitkImageStatisticsView::FillHistogramWidget(const std::vector &histogram, + const std::vector &dataLabels) +{ + m_Controls.groupBox_histogram->setVisible(true); + m_Controls.widget_histogram->SetTheme(GetColorTheme()); + m_Controls.widget_histogram->SetHistogram(histogram.front(), dataLabels.front()); + connect(m_Controls.widget_histogram, &QmitkHistogramVisualizationWidget::RequestHistogramUpdate, + this, &QmitkImageStatisticsView::OnRequestHistogramUpdate); +} + +QmitkChartWidget::ColorTheme QmitkImageStatisticsView::GetColorTheme() const +{ + ctkPluginContext *context = berry::WorkbenchPlugin::GetDefault()->GetPluginContext(); + ctkServiceReference styleManagerRef = context->getServiceReference(); + if (styleManagerRef) + { + auto styleManager = context->getService(styleManagerRef); + if (styleManager->GetStyle().name == "Dark") + { + return QmitkChartWidget::ColorTheme::darkstyle; + } + else + { + return QmitkChartWidget::ColorTheme::lightstyle; + } + } + return QmitkChartWidget::ColorTheme::darkstyle; +} + void QmitkImageStatisticsView::ResetGUI() { m_Controls.widget_statistics->Reset(); m_Controls.widget_statistics->setEnabled(false); m_Controls.widget_histogram->Reset(); m_Controls.widget_histogram->setEnabled(false); - m_Controls.checkBox_ignoreZero->setEnabled(false); } void QmitkImageStatisticsView::ResetGUIDefault() { m_Controls.widget_histogram->ResetDefault(); m_Controls.checkBox_ignoreZero->setChecked(false); + m_IgnoreZeroValueVoxel = false; } -std::string QmitkImageStatisticsView::GenerateStatisticsNodeName() -{ - std::string statisticsNodeName = ""; - - if (m_selectedImageNode.IsNotNull()) - { - statisticsNodeName = m_selectedImageNode->GetName();; - } - - if (m_selectedMaskNode.IsNotNull()) - { - statisticsNodeName += "_" + m_selectedMaskNode->GetName(); - } - statisticsNodeName += "statistics"; - - return statisticsNodeName; -} - -mitk::DataNode::Pointer QmitkImageStatisticsView::GetNodeForStatisticsContainer( - mitk::ImageStatisticsContainer::ConstPointer container) -{ - if (!container) - { - mitkThrow() << "Given container is null!"; - } - - auto allDataNodes = this->GetDataStorage()->GetAll()->CastToSTLConstContainer(); - for (auto node : allDataNodes) - { - auto nodeData = node->GetData(); - if (nodeData && nodeData->GetUID() == container->GetUID()) - { - return node; - } - } - mitkThrow() << "No DataNode is found which holds the given statistics container!"; -} - -void QmitkImageStatisticsView::HandleExistingStatistics(mitk::Image::ConstPointer image, - mitk::BaseData::ConstPointer mask, - mitk::ImageStatisticsContainer::Pointer statistic) +void QmitkImageStatisticsView::OnStatisticsCalculationEnds() { - auto imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics( - this->GetDataStorage(), image, mask); - - // if statistics base data already exist: add to existing node - if (imageStatistics) - { - auto node = GetNodeForStatisticsContainer(imageStatistics); - node->SetData(statistic); - } - // statistics base data does not exist: add new node - else + auto runnable = qobject_cast(sender()); + if (nullptr == runnable) { - auto statisticsNodeName = GenerateStatisticsNodeName(); - auto statisticsNode = mitk::CreateImageStatisticsNode(statistic, statisticsNodeName); - this->GetDataStorage()->Add(statisticsNode); + return; } -} -void QmitkImageStatisticsView::SetupRelationRules(mitk::ImageStatisticsContainer::Pointer statistic, - mitk::BaseData::ConstPointer mask) -{ - auto imageRule = mitk::StatisticsToImageRelationRule::New(); - imageRule->Connect(statistic, m_CalculationJob->GetStatisticsImage()); + mitk::StatusBar::GetInstance()->Clear(); - if (mask) + auto it = std::find(m_Runnables.begin(), m_Runnables.end(), runnable); + if (it != m_Runnables.end()) { - auto maskRule = mitk::StatisticsToMaskRelationRule::New(); - maskRule->Connect(statistic, mask); + m_Runnables.erase(it); } -} -void QmitkImageStatisticsView::OnStatisticsCalculationEnds() -{ - mitk::StatusBar::GetInstance()->Clear(); - - auto image = m_CalculationJob->GetStatisticsImage(); + auto image = runnable->GetStatisticsImage(); // get mask - mitk::BaseData::ConstPointer mask = nullptr; - if (m_CalculationJob->GetMaskImage()) + const mitk::BaseData* mask = nullptr; + if (runnable->GetMaskImage()) { - mask = m_CalculationJob->GetMaskImage(); + mask = runnable->GetMaskImage(); } - else if (m_CalculationJob->GetPlanarFigure()) + else if (runnable->GetPlanarFigure()) { - mask = m_CalculationJob->GetPlanarFigure(); + mask = runnable->GetPlanarFigure(); } // get current statistics auto currentImageStatistics = - mitk::ImageStatisticsContainerManager::GetImageStatistics(this->GetDataStorage(), image, mask); + mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image, mask); - if (this->m_CalculationJob->GetStatisticsUpdateSuccessFlag()) // case: calculation was successfull + if (runnable->GetStatisticsUpdateSuccessFlag()) // case: calculation was successful { - auto statistic = m_CalculationJob->GetStatisticsData(); + auto statistic = runnable->GetStatisticsData(); - SetupRelationRules(statistic, mask); + SetupRelationRules(image, mask, statistic); // checks for existing statistic, and add it to data manager HandleExistingStatistics(image, mask, statistic); - if (!m_selectedPlanarFigure || m_selectedPlanarFigure->IsClosed()) + auto maskPlanarFigure = dynamic_cast(mask); + if (!maskPlanarFigure || maskPlanarFigure->IsClosed()) { - this->FillHistogramWidget({m_CalculationJob->GetTimeStepHistogram()}, {m_selectedImageNode->GetName()}); + auto histogramLabelName = GenerateStatisticsNodeName(image, mask); + FillHistogramWidget({ runnable->GetTimeStepHistogram() }, { histogramLabelName }); } } - else // case: calculation was not successfull + else // case: calculation was not successful { // handle histogram auto emptyHistogram = HistogramType::New(); - this->FillHistogramWidget({emptyHistogram}, {m_selectedImageNode->GetName()}); + auto histogramLabelName = GenerateStatisticsNodeName(image, mask); + FillHistogramWidget({ emptyHistogram }, { histogramLabelName }); // handle statistics mitk::ImageStatisticsContainer::Pointer statistic = mitk::ImageStatisticsContainer::New(); statistic->SetTimeGeometry(const_cast(image->GetTimeGeometry())); - // add empty histogram to stastistics for all timesteps + // add empty histogram to statistics for all time steps for (unsigned int i = 0; i < image->GetTimeSteps(); ++i) { auto statisticObject = mitk::ImageStatisticsContainer::ImageStatisticsObject(); statisticObject.m_Histogram = emptyHistogram; statistic->SetStatisticsForTimeStep(i, statisticObject); } - SetupRelationRules(statistic, mask); + SetupRelationRules(image, mask, statistic); HandleExistingStatistics(image, mask, statistic); - mitk::StatusBar::GetInstance()->DisplayErrorText(m_CalculationJob->GetLastErrorMessage().c_str()); + mitk::StatusBar::GetInstance()->DisplayErrorText(runnable->GetLastErrorMessage().c_str()); m_Controls.widget_histogram->setEnabled(false); } + m_Controls.label_currentlyComputingStatistics->setVisible(false); } -void QmitkImageStatisticsView::OnRequestHistogramUpdate(unsigned int nBins) +void QmitkImageStatisticsView::OnRequestHistogramUpdate(unsigned int) { - m_CalculationJob->SetHistogramNBins(nBins); - m_CalculationJob->start(); + // NEEDS TO BE IMPLEMENTED - SEE T27032 } -void QmitkImageStatisticsView::CalculateStatistics(const mitk::Image *image, - const mitk::Image *mask, - const mitk::PlanarFigure *maskPlanarFigure) +void QmitkImageStatisticsView::OnCheckBoxIgnoreZeroStateChanged(int state) { - this->m_CalculationJob->Initialize(image, mask, maskPlanarFigure); + m_ForceRecompute = true; + m_IgnoreZeroValueVoxel = (state == Qt::Unchecked) ? false : true; - try - { - // Compute statistics - this->m_CalculationJob->start(); - m_Controls.label_currentlyComputingStatistics->setVisible(true); - } - catch (const mitk::Exception &e) + CalculateOrGetMultiStatistics(); + + m_ForceRecompute = false; +} + +void QmitkImageStatisticsView::OnButtonSelectionPressed() +{ + m_SelectionDialog = new QmitkNodeSelectionDialog(); + m_SelectionDialog->SetDataStorage(GetDataStorage()); + m_SelectionDialog->SetSelectionCheckFunction(CheckForSameGeometry()); + + // set predicates + auto isPlanarFigurePredicate = mitk::GetImageStatisticsPlanarFigurePredicate(); + auto isMaskPredicate = mitk::GetImageStatisticsMaskPredicate(); + auto isImagePredicate = mitk::GetImageStatisticsImagePredicate(); + auto isMaskOrPlanarFigurePredicate = mitk::NodePredicateOr::New(isPlanarFigurePredicate, isMaskPredicate); + auto isImageOrMaskOrPlanarFigurePredicate = mitk::NodePredicateOr::New(isMaskOrPlanarFigurePredicate, isImagePredicate); + m_SelectionDialog->SetNodePredicate(isImageOrMaskOrPlanarFigurePredicate); + m_SelectionDialog->SetSelectionMode(QAbstractItemView::MultiSelection); + connect(m_SelectionDialog, &QmitkNodeSelectionDialog::accepted, this, &QmitkImageStatisticsView::OnDialogSelectionChanged); + m_SelectionDialog->show(); +} + +void QmitkImageStatisticsView::OnDialogSelectionChanged() +{ + m_selectedImageNodes.resize(0); + m_selectedMaskNodes.resize(0); + + QmitkNodeSelectionDialog::NodeList selectedNodeList = m_SelectionDialog->GetSelectedNodes(); + auto isImagePredicate = mitk::GetImageStatisticsImagePredicate(); + for (const auto& node : selectedNodeList) { - mitk::StatusBar::GetInstance()->DisplayErrorText(e.GetDescription()); - m_Controls.label_currentlyComputingStatistics->setVisible(false); + if (isImagePredicate->CheckNode(node)) + { + m_selectedImageNodes.push_back(node.GetPointer()); + } + else + { + m_selectedMaskNodes.push_back(node.GetPointer()); + } } - catch (const std::runtime_error &e) + + if (!m_selectedImageNodes.empty()) { - mitk::StatusBar::GetInstance()->DisplayErrorText(e.what()); - m_Controls.label_currentlyComputingStatistics->setVisible(false); + m_Controls.widget_statistics->SetImageNodes(m_selectedImageNodes); } - catch (const std::exception &e) + + if (!m_selectedMaskNodes.empty()) { - mitk::StatusBar::GetInstance()->DisplayErrorText(e.what()); - m_Controls.label_currentlyComputingStatistics->setVisible(false); + m_Controls.widget_statistics->SetMaskNodes(m_selectedMaskNodes); } + + CalculateOrGetMultiStatistics(); } -void QmitkImageStatisticsView::OnSelectionChanged(berry::IWorkbenchPart::Pointer part, - const QList &nodes) +void QmitkImageStatisticsView::HandleExistingStatistics(mitk::Image::ConstPointer image, + mitk::BaseData::ConstPointer mask, + mitk::ImageStatisticsContainer::Pointer statistic) { - Q_UNUSED(part); - Q_UNUSED(nodes); + auto imageStatistics = mitk::ImageStatisticsContainerManager::GetImageStatistics(GetDataStorage(), image, mask); + + // if statistics base data already exist: add to existing node + if (imageStatistics) + { + auto node = GetNodeForStatisticsContainer(imageStatistics); + node->SetData(statistic); + } + // statistics base data does not exist: add new node + else + { + auto statisticsNodeName = GenerateStatisticsNodeName(image, mask); + auto statisticsNode = mitk::CreateImageStatisticsNode(statistic, statisticsNodeName); + GetDataStorage()->Add(statisticsNode); + } } -void QmitkImageStatisticsView::PrepareDataStorageComboBoxes() +std::string QmitkImageStatisticsView::GenerateStatisticsNodeName(mitk::Image::ConstPointer image, + mitk::BaseData::ConstPointer mask) { - auto isPlanarFigurePredicate = mitk::GetImageStatisticsPlanarFigurePredicate(); - auto isMaskPredicate = mitk::GetImageStatisticsMaskPredicate(); - auto isImagePredicate = mitk::GetImageStatisticsImagePredicate(); - auto isMaskOrPlanarFigurePredicate = mitk::NodePredicateOr::New(isPlanarFigurePredicate, isMaskPredicate); + std::string statisticsNodeName = "no"; - m_Controls.imageSelector->SetDataStorage(GetDataStorage()); - m_Controls.imageSelector->SetPredicate(isImagePredicate); + if (image.IsNotNull()) + { + statisticsNodeName = image->GetUID(); + } - m_Controls.maskImageSelector->SetDataStorage(GetDataStorage()); + if (mask.IsNotNull()) + { + statisticsNodeName += "_" + mask->GetUID(); + } - m_Controls.maskImageSelector->SetPredicate(isMaskOrPlanarFigurePredicate); - m_Controls.maskImageSelector->SetZeroEntryText(""); + statisticsNodeName += "_statistics"; + + return statisticsNodeName; } -void QmitkImageStatisticsView::Activated() {} +void QmitkImageStatisticsView::SetupRelationRules(mitk::Image::ConstPointer image, + mitk::BaseData::ConstPointer mask, + mitk::ImageStatisticsContainer::Pointer statistic) +{ + auto imageRule = mitk::StatisticsToImageRelationRule::New(); + imageRule->Connect(statistic, image); -void QmitkImageStatisticsView::Deactivated() {} + if (nullptr != mask) + { + auto maskRule = mitk::StatisticsToMaskRelationRule::New(); + maskRule->Connect(statistic, mask); + } +} -void QmitkImageStatisticsView::Visible() +mitk::DataNode::Pointer QmitkImageStatisticsView::GetNodeForStatisticsContainer(mitk::ImageStatisticsContainer::ConstPointer container) { - connect(this->m_Controls.imageSelector, - static_cast(&QComboBox::currentIndexChanged), - this, - &QmitkImageStatisticsView::OnImageSelectorChanged); - connect(this->m_Controls.maskImageSelector, - static_cast(&QComboBox::currentIndexChanged), - this, - &QmitkImageStatisticsView::OnMaskSelectorChanged); - OnImageSelectorChanged(); - OnMaskSelectorChanged(); + if (container.IsNull()) + { + mitkThrow() << "Given container is null!"; + } + + auto allDataNodes = GetDataStorage()->GetAll()->CastToSTLConstContainer(); + for (auto node : allDataNodes) + { + auto nodeData = node->GetData(); + if (nodeData && nodeData->GetUID() == container->GetUID()) + { + return node; + } + } + mitkThrow() << "No DataNode is found which holds the given statistics container!"; } -void QmitkImageStatisticsView::Hidden() +QmitkNodeSelectionDialog::SelectionCheckFunctionType QmitkImageStatisticsView::CheckForSameGeometry() const { - m_Controls.imageSelector->disconnect(); - m_Controls.maskImageSelector->disconnect(); + auto lambda = [this](const QmitkNodeSelectionDialog::NodeList& nodes) + { + if (nodes.empty()) + { + return std::string(); + } + + auto leftNode = nodes[0]; + for (int i = 1; i < nodes.size(); ++i) + { + auto rightNode = nodes[i]; + + bool sameGeometry = CheckForSameGeometry(leftNode, rightNode); + if (!sameGeometry) + { + std::stringstream ss; + ss << "

Invalid selection: All selected nodes must have the same geometry.

"; + return ss.str(); + } + } + + return std::string(); + }; + + return lambda; } -void QmitkImageStatisticsView::SetFocus() {} +bool QmitkImageStatisticsView::CheckForSameGeometry(const mitk::DataNode* node1, const mitk::DataNode* node2) const +{ + bool isSameGeometry(true); + + mitk::Image* image1 = dynamic_cast(node1->GetData()); + mitk::Image* image2 = dynamic_cast(node2->GetData()); + if (image1 && image2) + { + mitk::BaseGeometry* geo1 = image1->GetGeometry(); + mitk::BaseGeometry* geo2 = image2->GetGeometry(); + + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetOrigin(), geo2->GetOrigin()); + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(0), geo2->GetExtent(0)); + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(1), geo2->GetExtent(1)); + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetExtent(2), geo2->GetExtent(2)); + isSameGeometry = isSameGeometry && mitk::Equal(geo1->GetSpacing(), geo2->GetSpacing()); + isSameGeometry = isSameGeometry && mitk::MatrixEqualElementWise(geo1->GetIndexToWorldTransform()->GetMatrix(), geo2->GetIndexToWorldTransform()->GetMatrix()); + + return isSameGeometry; + } + else + { + return false; + } +} diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h index fcb64fede6..0ece1f2e6f 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h @@ -1,120 +1,110 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef QmitkImageStatisticsView_H__INCLUDED -#define QmitkImageStatisticsView_H__INCLUDED +#ifndef QMITKIMAGESTATISTICSVIEW_H +#define QMITKIMAGESTATISTICSVIEW_H #include "ui_QmitkImageStatisticsViewControls.h" -// Qmitk includes #include -#include +#include #include +#include -#include -#include #include /*! \brief QmitkImageStatisticsView is a bundle that allows statistics calculation from images. Three modes are supported: 1. Statistics of one image, 2. Statistics of an image and a segmentation, 3. Statistics of an image and a Planar Figure. The statistics calculation is realized in a separate thread to keep the gui accessible during calculation. \ingroup Plugins/org.mitk.gui.qt.measurementtoolbox */ -class QmitkImageStatisticsView : public QmitkAbstractView, public mitk::ILifecycleAwarePart, public berry::IPartListener +class QmitkImageStatisticsView : public QmitkAbstractView { Q_OBJECT public: - /*! - \brief default constructor */ - QmitkImageStatisticsView(QObject *parent = nullptr, const char *name = nullptr); + + static const std::string VIEW_ID; + /*! \brief default destructor */ ~QmitkImageStatisticsView() override; /*! - \brief method for creating the widget containing the application controls, like sliders, buttons etc. */ + \brief Creates the widget containing the application controls, like sliders, buttons etc.*/ void CreateQtPartControl(QWidget *parent) override; - /*! - \brief Is called from the selection mechanism once the data manager selection has changed*/ - void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &selectedNodes) override; - - static const std::string VIEW_ID; protected: - using HistogramType = mitk::ImageStatisticsContainer::HistogramType; - void Activated() override; - void Deactivated() override; - void Visible() override; - void Hidden() override; - void SetFocus() override; - - /** \brief Is called right before the view closes (before the destructor) */ - void PartClosed(const berry::IWorkbenchPartReference::Pointer&) override; + using HistogramType = mitk::ImageStatisticsContainer::HistogramType; - /** \brief Required for berry::IPartListener */ - Events::Types GetPartEventTypes() const override { return Events::CLOSED; } + void SetFocus() override { }; - void OnImageSelectorChanged(); - void OnMaskSelectorChanged(); + virtual void CreateConnections(); + void CalculateOrGetMultiStatistics(); void CalculateOrGetStatistics(); - void CalculateStatistics(const mitk::Image* image, - const mitk::Image* mask = nullptr, + void CalculateStatistics(const mitk::Image* image, const mitk::Image* mask = nullptr, const mitk::PlanarFigure* maskPlanarFigure = nullptr); void ComputeAndDisplayIntensityProfile(mitk::Image * image, mitk::PlanarFigure* maskPlanarFigure); void FillHistogramWidget(const std::vector &histogram, const std::vector &dataLabels); QmitkChartWidget::ColorTheme GetColorTheme() const; void ResetGUI(); void ResetGUIDefault(); - void PrepareDataStorageComboBoxes(); - /*! - \brief method for creating the connections of main and control widget */ - virtual void CreateConnections(); - void OnStatisticsCalculationEnds(); - void OnRequestHistogramUpdate(unsigned int nBins); + void OnRequestHistogramUpdate(unsigned int); void OnCheckBoxIgnoreZeroStateChanged(int state); - void OnSliderWidgetHistogramChanged(double value); - void OnSliderWidgetIntensityProfileChanged(); + void OnButtonSelectionPressed(); + void OnDialogSelectionChanged(); // member variable Ui::QmitkImageStatisticsViewControls m_Controls; private: - std::string GenerateStatisticsNodeName(); - - void HandleExistingStatistics(mitk::Image::ConstPointer image, - mitk::BaseData::ConstPointer mask, + void HandleExistingStatistics(mitk::Image::ConstPointer, mitk::BaseData::ConstPointer, mitk::ImageStatisticsContainer::Pointer); - void SetupRelationRules(mitk::ImageStatisticsContainer::Pointer, mitk::BaseData::ConstPointer mask); + std::string GenerateStatisticsNodeName(mitk::Image::ConstPointer, mitk::BaseData::ConstPointer); + + void SetupRelationRules(mitk::Image::ConstPointer, mitk::BaseData::ConstPointer, + mitk::ImageStatisticsContainer::Pointer); mitk::DataNode::Pointer GetNodeForStatisticsContainer(mitk::ImageStatisticsContainer::ConstPointer container); - typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; - QmitkImageStatisticsCalculationJob * m_CalculationJob = nullptr; - mitk::DataNode::ConstPointer m_selectedImageNode = nullptr, m_selectedMaskNode = nullptr; + QmitkNodeSelectionDialog::SelectionCheckFunctionType CheckForSameGeometry() const; + bool CheckForSameGeometry(const mitk::DataNode* node1, const mitk::DataNode* node2) const; + + typedef itk::SimpleMemberCommand ITKCommandType; + mitk::DataNode::ConstPointer m_selectedImageNode = nullptr; + mitk::DataNode::ConstPointer m_selectedMaskNode = nullptr; + mitk::PlanarFigure::Pointer m_selectedPlanarFigure = nullptr; + + QmitkNodeSelectionDialog* m_SelectionDialog = nullptr; - mitk::PlanarFigure::Pointer m_selectedPlanarFigure=nullptr; long m_PlanarFigureObserverTag; bool m_ForceRecompute = false; + bool m_IgnoreZeroValueVoxel = false; + std::vector m_selectedMaskNodes; + std::vector m_selectedImageNodes; + std::vector m_StatisticsForSelection; + std::vector m_Runnables; + }; -#endif // QmitkImageStatisticsView_H__INCLUDED + +#endif // QMITKIMAGESTATISTICSVIEW_H diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui index ca07213aba..85e13fdea2 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui @@ -1,472 +1,184 @@ QmitkImageStatisticsViewControls true 0 0 419 1016 Form - - - - QLayout::SetMinimumSize - - - 4 - - - - - - 0 - 0 - - - - false - - - - - - - - - - - - 255 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - - - 255 - 0 - 0 - - - - - - - 0 - 0 - 0 - - - - - - - - - 120 - 120 - 120 - - - - - - - 120 - 120 - 120 - - - - - - - - Calculating statistics... - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - - - 120 - 120 - 120 - - - - - - - - Mask image: - - - - - - - - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - - - 0 - 0 - 0 - - - - - - - - - 120 - 120 - 120 - - - - - - - - Image: - - - - - - - - 0 - 0 - - - - false - - - - - - - Ignore zero-valued voxels - - - - - 0 200 Statistics 3 3 3 3 - - - - - - - 255 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - - - 255 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - 255 - 0 - 0 - - - - - - - - - 120 - 120 - 120 - - - - - - - 120 - 120 - 120 - - - - - - - 120 - 120 - 120 - - - - - + + + 0 + 0 + + + + + + + + 0 + 0 + + + + Calculating statistics... + + + + + + + + + + + true + + + + + + + Ignore zero-valued voxels + + + + + 0 200 Histogram Qt::Vertical 20 40 0 200 Intensity Profile - - - - Qt::Vertical - - - - 20 - 40 - - - - QmitkImageStatisticsWidget QWidget
QmitkImageStatisticsWidget.h
1
- - QmitkDataStorageComboBox - QComboBox -
QmitkDataStorageComboBox.h
-
QmitkHistogramVisualizationWidget QWidget
QmitkHistogramVisualizationWidget.h
1
- - QmitkDataStorageComboBoxWithSelectNone - QComboBox -
QmitkDataStorageComboBoxWithSelectNone.h
-
QmitkIntensityProfileVisualizationWidget QWidget
QmitkIntensityProfileVisualizationWidget.h
1
ctkSliderWidget QWidget
ctkSliderWidget.h
1