diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/files.cmake b/Plugins/org.mitk.gui.qt.measurementtoolbox/files.cmake index 4fd1dd2406..74776764d3 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/files.cmake +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/files.cmake @@ -1,57 +1,59 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES QmitkMeasurementView.cpp QmitkPlanarFiguresTableModel.cpp QmitkImageStatisticsView.cpp + QmitkImageStatisticsCalculationThread.cpp mitkPluginActivator.cpp ) set(UI_FILES src/internal/QmitkImageStatisticsViewControls.ui ) set(MOC_H_FILES src/internal/QmitkMeasurementView.h src/internal/QmitkPlanarFiguresTableModel.h src/internal/QmitkImageStatisticsView.h + src/internal/QmitkImageStatisticsCalculationThread.h src/internal/mitkPluginActivator.h ) set(CACHED_RESOURCE_FILES resources/angle.png resources/arrow.png resources/circle.png resources/four-point-angle.png resources/ImageStatistic_24.png resources/ImageStatistic_48.png resources/ImageStatistic_64.png resources/lena.xpm resources/line.png resources/measurement.png resources/path.png resources/polygon.png resources/rectangle.png resources/stats.png resources/text.png plugin.xml ) set(QRC_FILES resources/measurement.qrc resources/QmitkImageStatisticsView.qrc ) set(CPP_FILES ) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.cpp new file mode 100644 index 0000000000..516fc76ba3 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.cpp @@ -0,0 +1,155 @@ +/*=================================================================== + +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 "QmitkImageStatisticsCalculationThread.h" + +//QT headers +#include +#include + +QmitkImageStatisticsCalculationThread::QmitkImageStatisticsCalculationThread():QThread(), + m_StatisticsImage(NULL), m_BinaryMask(NULL), m_PlanarFigureMask(NULL), m_TimeStep(0), + m_IgnoreZeros(false), m_CalculationSuccessful(false), m_StatisticChanged(false) +{ +} + +QmitkImageStatisticsCalculationThread::~QmitkImageStatisticsCalculationThread() +{ +} + +void QmitkImageStatisticsCalculationThread::Initialize( mitk::Image::Pointer image, mitk::Image::Pointer binaryImage, mitk::PlanarFigure::Pointer planarFig ) +{ + // reset old values + if( this->m_StatisticsImage.IsNotNull() ) + this->m_StatisticsImage = 0; + + if( this->m_BinaryMask.IsNotNull() ) + this->m_BinaryMask = 0; + + if( this->m_PlanarFigureMask.IsNotNull()) + this->m_PlanarFigureMask = 0; + + // set new values if passed in + if(image.IsNotNull()) + this->m_StatisticsImage = image->Clone(); + if(binaryImage.IsNotNull()) + this->m_BinaryMask = binaryImage->Clone(); + if(planarFig.IsNotNull()) + this->m_PlanarFigureMask = dynamic_cast(planarFig.GetPointer()); // once clone methods for planar figures are implemented, copy the data here! +} + +void QmitkImageStatisticsCalculationThread::SetTimeStep( int times ) +{ + this->m_TimeStep = times; +} + +int QmitkImageStatisticsCalculationThread::GetTimeStep() +{ + return this->m_TimeStep; +} + +mitk::ImageStatisticsCalculator::Statistics QmitkImageStatisticsCalculationThread::GetStatisticsData() +{ + return this->m_StatisticsStruct; +} + +mitk::Image::Pointer QmitkImageStatisticsCalculationThread::GetStatisticsImage() +{ + return this->m_StatisticsImage; +} + +void QmitkImageStatisticsCalculationThread::SetIgnoreZeroValueVoxel(bool _arg) +{ + this->m_IgnoreZeros = _arg; +} + +bool QmitkImageStatisticsCalculationThread::GetIgnoreZeroValueVoxel() +{ + return this->m_IgnoreZeros; +} + +QmitkImageStatisticsCalculationThread::HistogramType::Pointer +QmitkImageStatisticsCalculationThread::GetTimeStepHistogram() +{ + return this->m_TimeStepHistogram; +} + +bool QmitkImageStatisticsCalculationThread::GetStatisticsChangedFlag() +{ + return m_StatisticChanged; +} + +bool QmitkImageStatisticsCalculationThread::GetStatisticsUpdateSuccessFlag() +{ + return m_CalculationSuccessful; +} + +void QmitkImageStatisticsCalculationThread::run() +{ + bool statisticCalculationSuccessful = true; + mitk::ImageStatisticsCalculator::Pointer calculator = mitk::ImageStatisticsCalculator::New(); + + if(this->m_StatisticsImage.IsNotNull()) + { + calculator->SetImage(m_StatisticsImage); + calculator->SetMaskingModeToNone(); + } + else + { + statisticCalculationSuccessful = false; + } + if(this->m_BinaryMask.IsNotNull()) + { + calculator->SetImageMask(m_BinaryMask); + calculator->SetMaskingModeToImage(); + } + if(this->m_PlanarFigureMask.IsNotNull()) + { + calculator->SetPlanarFigure(m_PlanarFigureMask); + calculator->SetMaskingModeToPlanarFigure(); + } + bool statisticChanged = false; + + calculator->SetDoIgnorePixelValue(this->m_IgnoreZeros); + calculator->SetIgnorePixelValue(0); + try + { + statisticChanged = calculator->ComputeStatistics(m_TimeStep); + } + catch ( const std::runtime_error &e ) + { + MITK_ERROR<< "Runtime Exception: " << e.what(); + statisticCalculationSuccessful = false; + } + catch ( const std::exception &e ) + { + MITK_ERROR<< "Standard Exception: " << e.what(); + statisticCalculationSuccessful = false; + } + this->m_StatisticChanged = statisticChanged; + this->m_CalculationSuccessful = statisticCalculationSuccessful; + + if(statisticCalculationSuccessful) + { + this->m_StatisticsStruct = calculator->GetStatistics(m_TimeStep); + + if(this->m_TimeStepHistogram.IsNotNull()) + { + this->m_TimeStepHistogram = NULL; + } + this->m_TimeStepHistogram = (HistogramType*) calculator->GetHistogram(m_TimeStep); + } +} diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.h new file mode 100644 index 0000000000..0d4d7e6694 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsCalculationThread.h @@ -0,0 +1,100 @@ +/*=================================================================== + +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 QMITKIMAGESTATISTICSCALCULATIONTHREAD_H_INCLUDED +#define QMITKIMAGESTATISTICSCALCULATIONTHREAD_H_INCLUDED + +//QT headers +#include +#include + +//mitk headers +#include "mitkImage.h" +#include "mitkPlanarFigure.h" +#include "mitkImageStatisticsCalculator.h" + +// itk headers +#ifndef __itkHistogram_h +#include +#endif + + +/** /brief This class is executed as background thread for image statistics calculation. + * Documentation: This class is derived from QThread and is intended to be used by QmitkImageStatisticsView + to run the image statistics calculation in a background thread keepung the gui usable. + * \ingroup Plugins/MeasurementToolbox + */ + +class QmitkImageStatisticsCalculationThread : public QThread +{ + Q_OBJECT + +public: + + typedef itk::Statistics::Histogram HistogramType; + + /*! + /brief standard constructor. */ + QmitkImageStatisticsCalculationThread(); + /*! + /brief standard destructor. */ + ~QmitkImageStatisticsCalculationThread(); + /*! + /brief Initializes the object with necessary data. */ + void Initialize( mitk::Image::Pointer image, mitk::Image::Pointer binaryImage, mitk::PlanarFigure::Pointer planarFig ); + /*! + /brief returns the calculated image statistics. */ + mitk::ImageStatisticsCalculator::Statistics GetStatisticsData(); + /*! + /brief */ + mitk::Image::Pointer GetStatisticsImage(); + /*! + /brief Set the time step of the image you want to process. */ + void SetTimeStep( int times ); + /*! + /brief Get the time step of the image you want to process. */ + int GetTimeStep(); + /*! + /brief Set flag to ignore zero valued voxels */ + void SetIgnoreZeroValueVoxel( bool _arg ); + /*! + /brief Get status of zero value voxel ignoring. */ + bool GetIgnoreZeroValueVoxel(); + /*! + /brief Returns the histogram of the currently selected time step. */ + HistogramType::Pointer GetTimeStepHistogram(); + /*! + /brief Returns a flag indicating if the statistics have changed during calculation */ + bool GetStatisticsChangedFlag(); + /*! + /brief Returns a flag the indicates if the statistics are updated successfully */ + bool GetStatisticsUpdateSuccessFlag(); + /*! + /brief Method called once the thread is executed. */ + void run(); + +private: + //member declaration + mitk::Image::Pointer m_StatisticsImage; ///< member variable holds the input image for which the statistics need to be calculated. + mitk::Image::Pointer m_BinaryMask; ///< member variable holds the binary mask image for segmentation image statistics calculation. + mitk::PlanarFigure::Pointer m_PlanarFigureMask; ///< member variable holds the planar figure for segmentation image statistics calculation. + mitk::ImageStatisticsCalculator::Statistics m_StatisticsStruct; ///< member variable holds the result struct. + int m_TimeStep; ///< member variable holds the time step for statistics calculation + bool m_IgnoreZeros; ///< member variable holds flag to indicate if zero valued voxel should be suppressed + bool m_StatisticChanged; ///< flag set if statistics have changed + bool m_CalculationSuccessful; ///< flag set if statistics calculation was successful + HistogramType::Pointer m_TimeStepHistogram; ///< member holds the histogram of the current time step. +}; +#endif // QMITKIMAGESTATISTICSCALCULATIONTHREAD_H_INCLUDED 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 4937f57e0d..d80cc65ffa 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -1,805 +1,573 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #include "QmitkImageStatisticsView.h" -#include - -#include -#include -#include -#include -#include -#include -#include +// Qt includes #include -#include -#include +// berry includes #include -#include - - -#include "QmitkStdMultiWidget.h" -#include "QmitkSliderNavigatorWidget.h" +// mitk includes #include "mitkNodePredicateDataType.h" -#include "mitkImageTimeSelector.h" -#include "mitkProperties.h" - -#include "mitkProgressBar.h" - -// Includes for image processing -#include "mitkImageCast.h" -#include "mitkITKImageImport.h" - -#include "mitkDataNodeObject.h" -#include "mitkNodePredicateData.h" #include "mitkPlanarFigureInteractor.h" -#include - -const std::string QmitkImageStatisticsView::VIEW_ID = -"org.mitk.views.imagestatistics"; - -class QmitkRequestStatisticsUpdateEvent : public QEvent -{ -public: - enum Type - { - StatisticsUpdateRequest = QEvent::MaxUser - 1025 - }; - - QmitkRequestStatisticsUpdateEvent() - : QEvent( (QEvent::Type) StatisticsUpdateRequest ) {}; -}; +// itk includes +#include "itksys/SystemTools.hxx" - - -typedef itk::Image ImageType; -typedef itk::Image FloatImageType; -typedef itk::Image, 3> VectorImageType; - -inline bool my_isnan(float x) - { - volatile float d = x; - - if(d!=d) - return true; - - if(d==d) - return false; - return d != d; - - } +const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics"; QmitkImageStatisticsView::QmitkImageStatisticsView(QObject* /*parent*/, const char* /*name*/) : m_Controls( NULL ), - m_TimeStepperAdapter( NULL ), - m_SelectedImageNode( NULL ), - m_SelectedImage( NULL ), - m_SelectedMaskNode( NULL ), - m_SelectedImageMask( NULL ), - m_SelectedPlanarFigure( NULL ), - m_ImageObserverTag( -1 ), - m_ImageMaskObserverTag( -1 ), - m_PlanarFigureObserverTag( -1 ), - m_CurrentStatisticsValid( false ), - m_StatisticsUpdatePending( false ), - m_Visible(false) +m_TimeStepperAdapter( NULL ), +m_SelectedImage( NULL ), +m_SelectedImageMask( NULL ), +m_SelectedPlanarFigure( NULL ), +m_ImageObserverTag( -1 ), +m_ImageMaskObserverTag( -1 ), +m_PlanarFigureObserverTag( -1 ), +m_CurrentStatisticsValid( false ), +m_StatisticsUpdatePending( false ), +m_DataNodeSelectionChanged ( false ), +m_Visible(false) { + this->m_CalculationThread = new QmitkImageStatisticsCalculationThread; + this->m_SelectedDataNodes = SelectedDataNodeVectorType(2); // maximum number of selected nodes is exactly two! } - QmitkImageStatisticsView::~QmitkImageStatisticsView() { if ( m_SelectedImage != NULL ) m_SelectedImage->RemoveObserver( m_ImageObserverTag ); if ( m_SelectedImageMask != NULL ) m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); if ( m_SelectedPlanarFigure != NULL ) m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); -} + while(this->m_CalculationThread->isRunning()) // wait until thread has finished + { + itksys::SystemTools::Delay(100); + } + delete this->m_CalculationThread; +} void QmitkImageStatisticsView::CreateQtPartControl(QWidget *parent) { if (m_Controls == NULL) { m_Controls = new Ui::QmitkImageStatisticsViewControls; m_Controls->setupUi(parent); this->CreateConnections(); m_Controls->m_ErrorMessageLabel->hide(); - m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_LineProfileWidget->SetPathModeToPlanarFigure(); } } - - void QmitkImageStatisticsView::CreateConnections() { if ( m_Controls ) { - connect( (QObject*)(m_Controls->m_ButtonCopyHistogramToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(ClipboardHistogramButtonClicked())); - connect( (QObject*)(m_Controls->m_ButtonCopyStatisticsToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(ClipboardStatisticsButtonClicked())); - connect( (QObject*)(m_Controls->m_IgnoreZerosCheckbox), SIGNAL(clicked()),(QObject*) this, SLOT(IgnoreZerosCheckboxClicked())); + connect( (QObject*)(this->m_Controls->m_ButtonCopyHistogramToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(OnClipboardHistogramButtonClicked()) ); + connect( (QObject*)(this->m_Controls->m_ButtonCopyStatisticsToClipboard), SIGNAL(clicked()),(QObject*) this, SLOT(OnClipboardStatisticsButtonClicked()) ); + connect( (QObject*)(this->m_Controls->m_IgnoreZerosCheckbox), SIGNAL(clicked()),(QObject*) this, SLOT(OnIgnoreZerosCheckboxClicked()) ); + connect( (QObject*) this->m_CalculationThread, SIGNAL(finished()),this, SLOT( OnThreadedStatisticsCalculationEnds()),Qt::QueuedConnection); + connect( (QObject*) this, SIGNAL(StatisticsUpdate()),this, SLOT( RequestStatisticsUpdate()), Qt::QueuedConnection); } } -void QmitkImageStatisticsView::IgnoreZerosCheckboxClicked( ) +void QmitkImageStatisticsView::OnIgnoreZerosCheckboxClicked() { - UpdateStatistics(); + emit StatisticsUpdate(); } -void QmitkImageStatisticsView::ClipboardHistogramButtonClicked() +void QmitkImageStatisticsView::OnClipboardHistogramButtonClicked() { - if ( m_CurrentStatisticsValid && (m_CurrentStatisticsCalculator.IsNotNull()) ) + if ( m_CurrentStatisticsValid ) { typedef mitk::ImageStatisticsCalculator::HistogramType HistogramType; - const HistogramType *histogram = m_CurrentStatisticsCalculator->GetHistogram(); + const HistogramType *histogram = this->m_CalculationThread->GetTimeStepHistogram().GetPointer(); QString clipboard( "Measurement \t Frequency\n" ); for ( HistogramType::ConstIterator it = histogram->Begin(); - it != histogram->End(); - ++it ) + it != histogram->End(); + ++it ) { clipboard = clipboard.append( "%L1 \t %L2\n" ) .arg( it.GetMeasurementVector()[0], 0, 'f', 2 ) .arg( it.GetFrequency() ); } QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); } else { QApplication::clipboard()->clear(); } } - -void QmitkImageStatisticsView::ClipboardStatisticsButtonClicked() +void QmitkImageStatisticsView::OnClipboardStatisticsButtonClicked() { - if ( m_CurrentStatisticsValid && (m_CurrentStatisticsCalculator.IsNotNull()) ) + if ( this->m_CurrentStatisticsValid ) { const mitk::ImageStatisticsCalculator::Statistics &statistics = - m_CurrentStatisticsCalculator->GetStatistics(); + this->m_CalculationThread->GetStatisticsData(); // Copy statistics to clipboard ("%Ln" will use the default locale for // number formatting) QString clipboard( "Mean \t StdDev \t RMS \t Max \t Min \t N \t V (mm³)\n" ); clipboard = clipboard.append( "%L1 \t %L2 \t %L3 \t %L4 \t %L5 \t %L6 \t %L7" ) .arg( statistics.Mean, 0, 'f', 10 ) .arg( statistics.Sigma, 0, 'f', 10 ) .arg( statistics.RMS, 0, 'f', 10 ) .arg( statistics.Max, 0, 'f', 10 ) .arg( statistics.Min, 0, 'f', 10 ) .arg( statistics.N ) .arg( m_Controls->m_StatisticsTable->item( 0, 6 )->text() ); QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); } else { QApplication::clipboard()->clear(); } } - -void QmitkImageStatisticsView::FillStatisticsTableView( - const mitk::ImageStatisticsCalculator::Statistics &s, - const mitk::Image *image ) +void QmitkImageStatisticsView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*part*/, + const QList &selectedNodes ) { - m_Controls->m_StatisticsTable->setItem( 0, 0, new QTableWidgetItem( - QString("%1").arg(s.Mean, 0, 'f', 2) ) ); - - m_Controls->m_StatisticsTable->setItem( 0, 1, new QTableWidgetItem( - QString("%1").arg(s.Sigma, 0, 'f', 2) ) ); - - m_Controls->m_StatisticsTable->setItem( 0, 2, new QTableWidgetItem( - QString("%1").arg(s.RMS, 0, 'f', 2) ) ); - - m_Controls->m_StatisticsTable->setItem( 0, 3, new QTableWidgetItem( - QString("%1").arg(s.Max, 0, 'f', 2) ) ); - - m_Controls->m_StatisticsTable->setItem( 0, 4, new QTableWidgetItem( - QString("%1").arg(s.Min, 0, 'f', 2) ) ); - - m_Controls->m_StatisticsTable->setItem( 0, 5, new QTableWidgetItem( - QString("%1").arg(s.N) ) ); + this->SelectionChanged( selectedNodes ); +} - const mitk::Geometry3D *geometry = image->GetGeometry(); - if ( geometry != NULL ) +void QmitkImageStatisticsView::SelectionChanged(const QList &selectedNodes) +{ + if( this->m_StatisticsUpdatePending ) { - const mitk::Vector3D &spacing = image->GetGeometry()->GetSpacing(); - double volume = spacing[0] * spacing[1] * spacing[2] * (double) s.N; - m_Controls->m_StatisticsTable->setItem( 0, 6, new QTableWidgetItem( - QString("%1").arg(volume, 0, 'f', 2) ) ); + this->m_DataNodeSelectionChanged = true; + return; // not ready for new data now! } - else + this->ReinitData(); + if(selectedNodes.size() == 1 || selectedNodes.size() == 2) { - m_Controls->m_StatisticsTable->setItem( 0, 6, new QTableWidgetItem( - "NA" ) ); + for (int i= 0; i< selectedNodes.size(); ++i) + { + this->m_SelectedDataNodes.push_back(selectedNodes.at(i)); + } + this->m_DataNodeSelectionChanged = false; + this->m_Controls->m_ErrorMessageLabel->setText( "" ); + this->m_Controls->m_ErrorMessageLabel->hide(); + emit StatisticsUpdate(); } -} - - -void QmitkImageStatisticsView::InvalidateStatisticsTableView() -{ - for ( unsigned int i = 0; i < 7; ++i ) + else { - m_Controls->m_StatisticsTable->setItem( 0, i, new QTableWidgetItem( "NA" ) ); + this->m_DataNodeSelectionChanged = false; } } - -void QmitkImageStatisticsView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*part*/, - const QList &selectedNodes ) +void QmitkImageStatisticsView::ReinitData() { - // Clear any unreferenced images - this->RemoveOrphanImages(); - if ( !m_Visible || selectedNodes.isEmpty()) + while( this->m_CalculationThread->isRunning()) // wait until thread has finished { - return; + itksys::SystemTools::Delay(100); } - // Check if selection makeup consists only of valid nodes: - // One image, segmentation or planarFigure - // One image and one of the other two - bool tooManyNodes( true ); - bool invalidNodes( true ); - - if ( selectedNodes.size() < 3 ) + if(this->m_SelectedImage != NULL) { - tooManyNodes = false; + this->m_SelectedImage->RemoveObserver( this->m_ImageObserverTag); + this->m_SelectedImage = NULL; } - - QList nodes(selectedNodes); - if( !tooManyNodes ) + if(this->m_SelectedImageMask != NULL) + { + this->m_SelectedImageMask->RemoveObserver( this->m_ImageMaskObserverTag); + this->m_SelectedImageMask = NULL; + } + if(this->m_SelectedPlanarFigure != NULL) { - unsigned int numberImages = 0; - unsigned int numberSegmentations = 0; - unsigned int numberPlanarFigures = 0; + this->m_SelectedPlanarFigure->RemoveObserver( this->m_PlanarFigureObserverTag); + this->m_SelectedPlanarFigure = NULL; + } + this->m_SelectedDataNodes.clear(); + this->m_StatisticsUpdatePending = false; + + m_Controls->m_ErrorMessageLabel->setText( "" ); + m_Controls->m_ErrorMessageLabel->hide(); + this->InvalidateStatisticsTableView(); + m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); + m_Controls->m_HistogramWidget->ClearItemModel(); + m_Controls->m_LineProfileWidget->ClearItemModel(); +} - for ( int index = 0; index < nodes.size(); index++ ) - { - m_SelectedImageMask = dynamic_cast< mitk::Image * >( nodes[ index ]->GetData() ); - m_SelectedPlanarFigure = dynamic_cast< mitk::PlanarFigure * >( nodes[ index ]->GetData() ); +void QmitkImageStatisticsView::OnThreadedStatisticsCalculationEnds() +{ + std::stringstream message; + message << ""; + m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->hide(); + this->WriteStatisticsToGUI(); +} - bool isPositionMarker (false); - nodes[index]->GetBoolProperty("isContourMarker", isPositionMarker); +void QmitkImageStatisticsView::UpdateStatistics() +{ + mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); + if ( renderPart == NULL ) + { + this->m_StatisticsUpdatePending = false; + return; + } + // classify selected nodes + mitk::NodePredicateDataType::Pointer imagePredicate = mitk::NodePredicateDataType::New("Image"); + + std::string maskName = std::string(); + std::string maskType = std::string(); + unsigned int maskDimension = 0; + + // reset data from last run + ITKCommandType::Pointer changeListener = ITKCommandType::New(); + changeListener->SetCallbackFunction( this, &QmitkImageStatisticsView::SelectedDataModified ); + + mitk::Image::Pointer selectedImage = mitk::Image::New(); + for( int i= 0 ; i < this->m_SelectedDataNodes.size(); ++i) + { + mitk::PlanarFigure::Pointer planarFig = dynamic_cast(this->m_SelectedDataNodes.at(i)->GetData()); + if( imagePredicate->CheckNode(this->m_SelectedDataNodes.at(i)) ) + { + bool isMask = false; + this->m_SelectedDataNodes.at(i)->GetPropertyValue("binary", isMask); - if ( m_SelectedImageMask != NULL ) + if( this->m_SelectedImageMask == NULL && isMask) { - bool isMask( false ); - nodes[ index ]->GetPropertyValue("binary", isMask); - if ( !isMask ) - { - numberImages++; - } - else - { - numberSegmentations++; - if ( numberImages != 0 ) // image should be last element - { - std::swap( nodes[ index ], nodes[ index - 1 ] ); - } - } + this->m_SelectedImageMask = dynamic_cast(this->m_SelectedDataNodes.at(i)->GetData()); + this->m_ImageMaskObserverTag = this->m_SelectedImageMask->AddObserver(itk::ModifiedEvent(), changeListener); + + maskName = this->m_SelectedDataNodes.at(i)->GetName(); + maskType = m_SelectedImageMask->GetNameOfClass(); + maskDimension = 3; } - else if ( m_SelectedPlanarFigure != NULL && !isPositionMarker) + else if( !isMask ) { - numberPlanarFigures++; - if ( numberImages != 0 ) // image should be last element + if(this->m_SelectedImage == NULL) { - std::swap( nodes[ index ], nodes[ index - 1 ] ); + this->m_SelectedImage = static_cast(this->m_SelectedDataNodes.at(i)->GetData()); + this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener); } } } - - if ( ( numberPlanarFigures + numberSegmentations + numberImages ) == nodes.size() && //No invalid nodes - ( numberPlanarFigures + numberSegmentations ) < 2 && numberImages < 2 - // maximum of one image and/or one of either planar figure or segmentation - ) + else if (planarFig.IsNotNull()) { - invalidNodes = false; - } - } - - if ( nodes.empty() || tooManyNodes || invalidNodes ) - { - ClearObservers(); - // Nothing to do: invalidate image, clear statistics, histogram, and GUI - this->InvalidateStatisticsTableView() ; - m_Controls->m_HistogramWidget->ClearItemModel(); - m_Controls->m_LineProfileWidget->ClearItemModel(); - - m_CurrentStatisticsValid = false; - m_Controls->m_ErrorMessageLabel->hide(); - m_Controls->m_SelectedMaskLabel->setText( "None" ); - - - return; - } - - // Get selected element - - mitk::DataNode *selectedNode = nodes.front(); - mitk::Image *selectedImage = dynamic_cast< mitk::Image * >( selectedNode->GetData() ); - - // Find the next parent/grand-parent node containing an image, if any - mitk::DataStorage::SetOfObjects::ConstPointer parentObjects; - mitk::DataNode *parentNode = NULL; - mitk::Image *parentImage = NULL; - - ClearObservers(); - { - unsigned int parentObjectIndex = 0; - parentObjects = this->GetDataStorage()->GetSources( selectedNode ); - while( parentObjectIndex < parentObjects->Size() ) - { - // Use first parent object (if multiple parents are present) - parentNode = parentObjects->ElementAt( parentObjectIndex ); - parentImage = dynamic_cast< mitk::Image * >( parentNode->GetData() ); - if( parentImage != NULL ) - { - break; - } - parentObjectIndex++; - } - } - - if ( nodes.size() == 2 ) - { - parentNode = nodes.back(); - parentImage = dynamic_cast< mitk::Image * >( parentNode->GetData() ); - } - - if ( parentImage != NULL ) - { - m_SelectedImageNode = parentNode; - m_SelectedImage = parentImage; - - // Check if a valid mask has been selected (Image or PlanarFigure) - m_SelectedImageMask = dynamic_cast< mitk::Image * >( selectedNode->GetData() ); - m_SelectedPlanarFigure = dynamic_cast< mitk::PlanarFigure * >( selectedNode->GetData() ); - - // Check whether ImageMask is a binary segmentation - - if ( (m_SelectedImageMask != NULL) ) - { - bool isMask( false ); - selectedNode->GetPropertyValue("binary", isMask); - if ( !isMask ) - { - m_SelectedImageNode = selectedNode; - m_SelectedImage = selectedImage; - m_SelectedImageMask = NULL; - } - else + if(this->m_SelectedPlanarFigure == NULL) { - m_SelectedMaskNode = selectedNode; + this->m_SelectedPlanarFigure = planarFig; + this->m_PlanarFigureObserverTag = + this->m_SelectedPlanarFigure->AddObserver(mitk::EndInteractionPlanarFigureEvent(), changeListener); + maskName = this->m_SelectedDataNodes.at(i)->GetName(); + maskType = this->m_SelectedPlanarFigure->GetNameOfClass(); + maskDimension = 2; } } - else if ( (m_SelectedPlanarFigure != NULL) ) + else { - m_SelectedMaskNode = selectedNode; + std::stringstream message; + message << "" << "Invalid data node type!" << ""; + m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->show(); } } - else if ( selectedImage != NULL ) - { - m_SelectedImageNode = selectedNode; - m_SelectedImage = selectedImage; - } - - - typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; - ITKCommandType::Pointer changeListener; - changeListener = ITKCommandType::New(); - changeListener->SetCallbackFunction( this, &QmitkImageStatisticsView::RequestStatisticsUpdate ); - - // Add change listeners to selected objects - if ( m_SelectedImage != NULL ) - { - m_ImageObserverTag = m_SelectedImage->AddObserver( - itk::ModifiedEvent(), changeListener ); - } - - if ( m_SelectedImageMask != NULL ) - { - m_ImageMaskObserverTag = m_SelectedImageMask->AddObserver( - itk::ModifiedEvent(), changeListener ); - } - - if ( m_SelectedPlanarFigure != NULL ) - { - m_PlanarFigureObserverTag = m_SelectedPlanarFigure->AddObserver( - mitk::EndInteractionPlanarFigureEvent(), changeListener ); - } - - - // Clear statistics / histogram GUI if nothing is selected - if ( m_SelectedImage == NULL ) - { - // Clear statistics, histogram, and GUI - this->InvalidateStatisticsTableView(); - m_Controls->m_HistogramWidget->ClearItemModel(); - m_Controls->m_LineProfileWidget->ClearItemModel(); - m_CurrentStatisticsValid = false; - m_Controls->m_ErrorMessageLabel->hide(); - m_Controls->m_SelectedMaskLabel->setText( "None" ); - } - else - { - // Else, request statistics and GUI update - this->RequestStatisticsUpdate(); - } -} - - -void QmitkImageStatisticsView::UpdateStatistics() -{ - // Remove any cached images that are no longer referenced elsewhere - this->RemoveOrphanImages(); - - mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); - if ( renderPart == NULL ) + if(maskName == "") { - return; + maskName = "None"; + maskType = ""; + maskDimension = 0; } unsigned int timeStep = renderPart->GetTimeNavigationController()->GetTime()->GetPos(); - - if ( m_SelectedImage != NULL ) + + if ( m_SelectedImage != NULL && m_SelectedImage->IsInitialized()) { // Check if a the selected image is a multi-channel image. If yes, statistics // cannot be calculated currently. if ( m_SelectedImage->GetPixelType().GetNumberOfComponents() > 1 ) { std::stringstream message; message << "Multi-component images not supported."; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); this->InvalidateStatisticsTableView(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->ClearItemModel(); + m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; + this->m_StatisticsUpdatePending = false; return; } - // Retrieve ImageStatisticsCalculator from has map (or create a new one - // for this image if non-existant) - ImageStatisticsMapType::iterator it = - m_ImageStatisticsMap.find( m_SelectedImage ); - - if ( it != m_ImageStatisticsMap.end() ) - { - m_CurrentStatisticsCalculator = it->second; - MITK_INFO << "Retrieving StatisticsCalculator"; - } - else - { - m_CurrentStatisticsCalculator = mitk::ImageStatisticsCalculator::New(); - m_CurrentStatisticsCalculator->SetImage( m_SelectedImage ); - m_ImageStatisticsMap[m_SelectedImage] = m_CurrentStatisticsCalculator; - MITK_INFO << "Creating StatisticsCalculator"; - } - - std::string maskName; - std::string maskType; - unsigned int maskDimension; - - if ( m_SelectedImageMask != NULL ) - { - m_CurrentStatisticsCalculator->SetImageMask( m_SelectedImageMask ); - m_CurrentStatisticsCalculator->SetMaskingModeToImage(); - - maskName = m_SelectedMaskNode->GetName(); - maskType = m_SelectedImageMask->GetNameOfClass(); - maskDimension = 3; - } - else if ( m_SelectedPlanarFigure != NULL ) - { - m_CurrentStatisticsCalculator->SetPlanarFigure( m_SelectedPlanarFigure ); - m_CurrentStatisticsCalculator->SetMaskingModeToPlanarFigure(); - - maskName = m_SelectedMaskNode->GetName(); - maskType = m_SelectedPlanarFigure->GetNameOfClass(); - maskDimension = 2; - } - else - { - m_CurrentStatisticsCalculator->SetMaskingModeToNone(); - - maskName = "None"; - maskType = ""; - maskDimension = 0; - } - - if(m_Controls->m_IgnoreZerosCheckbox->isChecked()) - { - m_CurrentStatisticsCalculator->SetIgnorePixelValue(0); - m_CurrentStatisticsCalculator->SetDoIgnorePixelValue(true); - } - else - { - m_CurrentStatisticsCalculator->SetDoIgnorePixelValue(false); - } - std::stringstream maskLabel; maskLabel << maskName; if ( maskDimension > 0 ) { maskLabel << " [" << maskDimension << "D " << maskType << "]"; } - m_Controls->m_SelectedMaskLabel->setText( maskLabel.str().c_str() ); + + // check time step validity + if(m_SelectedImage->GetDimension() <= 3 && timeStep > m_SelectedImage->GetDimension(3)-1) + { + timeStep = m_SelectedImage->GetDimension(3)-1; + } - bool statisticsChanged = false; - bool statisticsCalculationSuccessful = false; - - // Initialize progress bar - mitk::ProgressBar::GetInstance()->AddStepsToDo( 100 ); - - // Install listener for progress events and initialize progress bar - typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; - ITKCommandType::Pointer progressListener; - progressListener = ITKCommandType::New(); - progressListener->SetCallbackFunction( this, &QmitkImageStatisticsView::UpdateProgressBar ); - unsigned long progressObserverTag = m_CurrentStatisticsCalculator - ->AddObserver( itk::ProgressEvent(), progressListener ); - - // show wait cursor - this->WaitCursorOn(); + //// initialize thread and trigger it + this->m_CalculationThread->SetIgnoreZeroValueVoxel( m_Controls->m_IgnoreZerosCheckbox->isChecked() ); + this->m_CalculationThread->Initialize( m_SelectedImage, m_SelectedImageMask, m_SelectedPlanarFigure ); + this->m_CalculationThread->SetTimeStep( timeStep ); + std::stringstream message; + message << "Calculating statistics..."; + m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->show(); try { // Compute statistics - statisticsChanged = - m_CurrentStatisticsCalculator->ComputeStatistics( timeStep ); - - statisticsCalculationSuccessful = true; + this->m_CalculationThread->start(); } catch ( const std::runtime_error &e ) { // In case of exception, print error message on GUI std::stringstream message; message << "" << e.what() << ""; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); + this->m_StatisticsUpdatePending = false; } catch ( const std::exception &e ) { MITK_ERROR << "Caught exception: " << e.what(); // In case of exception, print error message on GUI std::stringstream message; message << "Error! Unequal Dimensions of Image and Segmentation. No recompute possible "; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); - } - - m_CurrentStatisticsCalculator->RemoveObserver( progressObserverTag ); - - // Make sure that progress bar closes - mitk::ProgressBar::GetInstance()->Progress( 100 ); - - // remove wait cursor - this->WaitCursorOff(); - - if ( statisticsCalculationSuccessful ) - { - if ( statisticsChanged ) - { - // Do not show any error messages - m_Controls->m_ErrorMessageLabel->hide(); - - m_CurrentStatisticsValid = true; - } - - m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); - m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); - m_Controls->m_HistogramWidget->SetHistogram( - m_CurrentStatisticsCalculator->GetHistogram( timeStep ) ); - m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); - - MITK_INFO << "UpdateItemModelFromHistogram()"; - - this->FillStatisticsTableView( - m_CurrentStatisticsCalculator->GetStatistics( timeStep ), - m_SelectedImage ); - } - else - { - m_Controls->m_SelectedMaskLabel->setText( "None" ); - - // Clear statistics and histogram - this->InvalidateStatisticsTableView(); - m_Controls->m_HistogramWidget->ClearItemModel(); - m_CurrentStatisticsValid = false; - - - // If a (non-closed) PlanarFigure is selected, display a line profile widget - if ( m_SelectedPlanarFigure != NULL ) - { - // check whether PlanarFigure is initialized - const mitk::Geometry2D *planarFigureGeometry2D = m_SelectedPlanarFigure->GetGeometry2D(); - if ( planarFigureGeometry2D == NULL ) - { - // Clear statistics, histogram, and GUI - this->InvalidateStatisticsTableView(); - m_Controls->m_HistogramWidget->ClearItemModel(); - m_Controls->m_LineProfileWidget->ClearItemModel(); - m_CurrentStatisticsValid = false; - m_Controls->m_ErrorMessageLabel->hide(); - m_Controls->m_SelectedMaskLabel->setText( "None" ); - return; - } - // TODO: enable line profile widget - m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 1 ); - m_Controls->m_LineProfileWidget->SetImage( m_SelectedImage ); - m_Controls->m_LineProfileWidget->SetPlanarFigure( m_SelectedPlanarFigure ); - m_Controls->m_LineProfileWidget->UpdateItemModelFromPath(); - } + this->m_StatisticsUpdatePending = false; } } + else + { + this->m_StatisticsUpdatePending = false; + } } -void QmitkImageStatisticsView::UpdateProgressBar() +void QmitkImageStatisticsView::SelectedDataModified() { - mitk::ProgressBar::GetInstance()->Progress(); + if( !m_StatisticsUpdatePending ) + { + emit StatisticsUpdate(); + } } - -void QmitkImageStatisticsView::RequestStatisticsUpdate() +void QmitkImageStatisticsView::NodeRemoved(const mitk::DataNode *node) { - if ( !m_StatisticsUpdatePending ) + while(this->m_CalculationThread->isRunning()) // wait until thread has finished { - QApplication::postEvent( this, new QmitkRequestStatisticsUpdateEvent ); - m_StatisticsUpdatePending = true; + itksys::SystemTools::Delay(100); } -} + if (node->GetData() == m_SelectedImage) + { + m_SelectedImage = NULL; + } +} -void QmitkImageStatisticsView::RemoveOrphanImages() +void QmitkImageStatisticsView::RequestStatisticsUpdate() { - ImageStatisticsMapType::iterator it = m_ImageStatisticsMap.begin(); - - while ( it != m_ImageStatisticsMap.end() ) + if ( !m_StatisticsUpdatePending ) { - mitk::Image *image = it->first; - mitk::ImageStatisticsCalculator *calculator = it->second; - ++it; - - mitk::NodePredicateData::Pointer hasImage = mitk::NodePredicateData::New( image ); - if ( this->GetDataStorage()->GetNode( hasImage ) == NULL ) + if(this->m_DataNodeSelectionChanged) { - if ( m_SelectedImage == image ) - { - m_SelectedImage = NULL; - m_SelectedImageNode = NULL; - } - if ( m_CurrentStatisticsCalculator == calculator ) - { - m_CurrentStatisticsCalculator = NULL; - } - m_ImageStatisticsMap.erase( image ); - it = m_ImageStatisticsMap.begin(); + this->SelectionChanged( this->GetDataManagerSelection()); + } + else + { + this->m_StatisticsUpdatePending = true; + this->UpdateStatistics(); } } + if (this->GetRenderWindowPart()) + this->GetRenderWindowPart()->RequestUpdate(); } - -bool QmitkImageStatisticsView::event( QEvent *event ) +void QmitkImageStatisticsView::WriteStatisticsToGUI() { - if ( event->type() == (QEvent::Type) QmitkRequestStatisticsUpdateEvent::StatisticsUpdateRequest ) + if(m_DataNodeSelectionChanged) { - // Update statistics + this->m_StatisticsUpdatePending = false; + this->RequestStatisticsUpdate(); + return; // stop visualization of results and calculate statistics of new selection + } - m_StatisticsUpdatePending = false; + if ( this->m_CalculationThread->GetStatisticsUpdateSuccessFlag()) + { + if ( this->m_CalculationThread->GetStatisticsChangedFlag() ) + { + // Do not show any error messages + m_Controls->m_ErrorMessageLabel->hide(); + m_CurrentStatisticsValid = true; + } - this->UpdateStatistics(); - return true; + m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); + m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); + m_Controls->m_HistogramWidget->SetHistogram( this->m_CalculationThread->GetTimeStepHistogram().GetPointer() ); + m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); + int timeStep = this->m_CalculationThread->GetTimeStep(); + this->FillStatisticsTableView( this->m_CalculationThread->GetStatisticsData(), this->m_CalculationThread->GetStatisticsImage()); } + else + { + m_Controls->m_SelectedMaskLabel->setText( "None" ); + std::stringstream message; + message << "Error calculating statistics!"; + m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); + m_Controls->m_ErrorMessageLabel->show(); + // Clear statistics and histogram + this->InvalidateStatisticsTableView(); + m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); + m_Controls->m_HistogramWidget->ClearItemModel(); + m_Controls->m_LineProfileWidget->ClearItemModel(); + m_CurrentStatisticsValid = false; - return false; + // If a (non-closed) PlanarFigure is selected, display a line profile widget + if ( m_SelectedPlanarFigure != NULL ) + { + // check whether PlanarFigure is initialized + const mitk::Geometry2D *planarFigureGeometry2D = m_SelectedPlanarFigure->GetGeometry2D(); + if ( planarFigureGeometry2D == NULL ) + { + // Clear statistics, histogram, and GUI + this->InvalidateStatisticsTableView(); + m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); + m_Controls->m_HistogramWidget->ClearItemModel(); + m_Controls->m_LineProfileWidget->ClearItemModel(); + m_CurrentStatisticsValid = false; + m_Controls->m_ErrorMessageLabel->hide(); + m_Controls->m_SelectedMaskLabel->setText( "None" ); + this->m_StatisticsUpdatePending = false; + return; + } + // TODO: enable line profile widget + m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 1 ); + m_Controls->m_LineProfileWidget->SetImage( this->m_CalculationThread->GetStatisticsImage() ); + m_Controls->m_LineProfileWidget->SetPlanarFigure( m_SelectedPlanarFigure ); + m_Controls->m_LineProfileWidget->UpdateItemModelFromPath(); + } + } + this->m_StatisticsUpdatePending = false; } void QmitkImageStatisticsView::ComputeIntensityProfile( mitk::PlanarLine* line ) { double sampling = 300; QmitkVtkHistogramWidget::HistogramType::Pointer histogram = QmitkVtkHistogramWidget::HistogramType::New(); itk::Size<1> siz; siz[0] = sampling; itk::FixedArray lower, higher; lower.Fill(0); - mitk::Point3D begin = line->GetWorldControlPoint(0); + mitk::Point3D begin = line->GetWorldControlPoint(0); mitk::Point3D end = line->GetWorldControlPoint(1); itk::Vector direction = (end - begin); higher.Fill(direction.GetNorm()); histogram->Initialize(siz, lower, higher); for(int i = 0; i < sampling; i++) { - //mitk::Point3D location = begin + double(i)/sampling * direction; double d = m_SelectedImage->GetPixelValueByWorldCoordinate(begin + double(i)/sampling * direction); histogram->SetFrequency(i,d); } m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); m_Controls->m_HistogramWidget->SetHistogram( histogram ); m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); +} + +void QmitkImageStatisticsView::FillStatisticsTableView( + const mitk::ImageStatisticsCalculator::Statistics &s, + const mitk::Image *image ) +{ + this->m_Controls->m_StatisticsTable->setItem( 0, 0, new QTableWidgetItem( + QString("%1").arg(s.Mean, 0, 'f', 2) ) ); + this->m_Controls->m_StatisticsTable->setItem( 0, 1, new QTableWidgetItem( + QString("%1").arg(s.Sigma, 0, 'f', 2) ) ); + + this->m_Controls->m_StatisticsTable->setItem( 0, 2, new QTableWidgetItem( + QString("%1").arg(s.RMS, 0, 'f', 2) ) ); + + this->m_Controls->m_StatisticsTable->setItem( 0, 3, new QTableWidgetItem( + QString("%1").arg(s.Max, 0, 'f', 2) ) ); + + this->m_Controls->m_StatisticsTable->setItem( 0, 4, new QTableWidgetItem( + QString("%1").arg(s.Min, 0, 'f', 2) ) ); + this->m_Controls->m_StatisticsTable->setItem( 0, 5, new QTableWidgetItem( + QString("%1").arg(s.N) ) ); + + const mitk::Geometry3D *geometry = image->GetGeometry(); + if ( geometry != NULL ) + { + const mitk::Vector3D &spacing = image->GetGeometry()->GetSpacing(); + double volume = spacing[0] * spacing[1] * spacing[2] * (double) s.N; + this->m_Controls->m_StatisticsTable->setItem( 0, 6, new QTableWidgetItem( + QString("%1").arg(volume, 0, 'f', 2) ) ); + } + else + { + this->m_Controls->m_StatisticsTable->setItem( 0, 6, new QTableWidgetItem( + "NA" ) ); + } } +void QmitkImageStatisticsView::InvalidateStatisticsTableView() +{ + for ( unsigned int i = 0; i < 7; ++i ) + { + this->m_Controls->m_StatisticsTable->setItem( 0, i, new QTableWidgetItem( "NA" ) ); + } +} void QmitkImageStatisticsView::Activated() { } void QmitkImageStatisticsView::Deactivated() { } void QmitkImageStatisticsView::Visible() { m_Visible = true; this->OnSelectionChanged(this->GetSite()->GetPage()->FindView("org.mitk.views.datamanager"), - this->GetDataManagerSelection()); + this->GetDataManagerSelection()); } void QmitkImageStatisticsView::Hidden() { m_Visible = false; } void QmitkImageStatisticsView::SetFocus() { - -} - -void QmitkImageStatisticsView::ClearObservers() -{ - // Possibly previous change listeners - if ( (m_SelectedPlanarFigure != NULL) && (m_PlanarFigureObserverTag >= 0) ) - { - m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); - m_PlanarFigureObserverTag = -1; - } - if ( (m_SelectedImage != NULL) && (m_ImageObserverTag >= 0) ) - { - m_SelectedImage->RemoveObserver( m_ImageObserverTag ); - m_ImageObserverTag = -1; - } - if ( (m_SelectedImageMask != NULL) && (m_ImageMaskObserverTag >= 0) ) - { - m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); - m_ImageMaskObserverTag = -1; - } - - // Deselect all images and masks by default - m_SelectedImageNode = NULL; - m_SelectedImage = NULL; - m_SelectedMaskNode = NULL; - m_SelectedImageMask = NULL; - m_SelectedPlanarFigure = NULL; - - return; } 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 d826a6b80f..e171550531 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h @@ -1,169 +1,158 @@ /*=================================================================== 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 QmitkImageStatisticsView_H__INCLUDED #define QmitkImageStatisticsView_H__INCLUDED #include "ui_QmitkImageStatisticsViewControls.h" +// Qmitk includes #include -#include - #include "QmitkStepperAdapter.h" +#include "QmitkImageStatisticsCalculationThread.h" +// mitk includes #include "mitkImageStatisticsCalculator.h" - -#include +#include "mitkILifecycleAwarePart.h" #include "mitkPlanarLine.h" - /*! -\brief QmitkImageStatisticsView +\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 seperate thread to keep the + gui accessable during calculation. -\sa QmitkFunctionality -\ingroup Functionalities +\ingroup Plugins/org.mitk.gui.qt.measurementtoolbox */ class QmitkImageStatisticsView : public QmitkAbstractView, public mitk::ILifecycleAwarePart { Q_OBJECT public: - /*! - \ Convenient typedefs - */ + \ Convenient typedefs */ typedef mitk::DataStorage::SetOfObjects ConstVector; typedef ConstVector::ConstPointer ConstVectorPointer; typedef ConstVector::ConstIterator ConstVectorIterator; - + typedef std::map< mitk::Image *, mitk::ImageStatisticsCalculator::Pointer > ImageStatisticsMapType; + typedef std::vector SelectedDataNodeVectorType; + typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; /*! - \brief default constructor - */ + \brief default constructor */ QmitkImageStatisticsView(QObject *parent=0, const char *name=0); - /*! - \brief default destructor - */ + \brief default destructor */ virtual ~QmitkImageStatisticsView(); - /*! - \brief method for creating the widget containing the application controls, like sliders, buttons etc. - */ + \brief method for creating the widget containing the application controls, like sliders, buttons etc. */ virtual void CreateQtPartControl(QWidget *parent); - /*! - \brief method for creating the connections of main and control widget - */ + \brief method for creating the connections of main and control widget */ virtual void CreateConnections(); - - bool IsExclusiveFunctionality() const; - - virtual bool event( QEvent *event ); - + /*! + \brief not implemented*/ + //bool IsExclusiveFunctionality() const; + /*! + \brief Is called from the selection mechanism once the data manager selection has changed*/ void OnSelectionChanged( berry::IWorkbenchPart::Pointer part, const QList &nodes ); static const std::string VIEW_ID; -protected slots: - void ClipboardHistogramButtonClicked(); - - void ClipboardStatisticsButtonClicked(); +public slots: + /** \brief Called when the statistics update is finished, sets the results to GUI.*/ + void OnThreadedStatisticsCalculationEnds(); - void IgnoreZerosCheckboxClicked( ); +protected slots: + /** \brief Saves the histogram to the clipboard */ + void OnClipboardHistogramButtonClicked(); + /** \brief Saves the statistics to the clipboard */ + void OnClipboardStatisticsButtonClicked(); + /** \brief Indicates if zeros should be excluded from statistics calculation */ + void OnIgnoreZerosCheckboxClicked( ); + /** \brief Checks if update is possible and calls StatisticsUpdate() possible */ + void RequestStatisticsUpdate(); +signals: + /** \brief Method to set the data to the member and start the threaded statistics update */ + void StatisticsUpdate(); protected: - + /** \brief Writes the calculated statistics to the GUI */ void FillStatisticsTableView( const mitk::ImageStatisticsCalculator::Statistics &s, const mitk::Image *image ); - + /** \brief Removes statistics from the GUI */ void InvalidateStatisticsTableView(); - /** \brief Issues a request to update statistics by sending an event to the - * Qt event processing queue. - * - * Statistics update should only be executed after program execution returns - * to the Qt main loop. This mechanism also prevents multiple execution of - * updates where only one is required.*/ - void RequestStatisticsUpdate(); - /** \brief Recalculate statistics for currently selected image and mask and * update the GUI. */ void UpdateStatistics(); /** \brief Listener for progress events to update progress bar. */ void UpdateProgressBar(); - /** \brief Removes any cached images which are no longer referenced elsewhere. */ void RemoveOrphanImages(); /** \brief Computes an Intensity Profile along line and updates the histogram widget with it. */ void ComputeIntensityProfile( mitk::PlanarLine* line ); /** \brief Removes all Observers to images, masks and planar figures and sets corresponding members to zero */ void ClearObservers(); void Activated(); void Deactivated(); - void Visible(); void Hidden(); - void SetFocus(); + /** \brief Method called when itkModifiedEvent is called by selected data. */ + void SelectedDataModified(); + /** \brief Method called when the data manager selection changes */ + void SelectionChanged(const QList &selectedNodes); + /** \brief Method called to remove old selection when a new selection is present */ + void ReinitData(); + /** \brief writes the statistics to the gui*/ + void WriteStatisticsToGUI(); - typedef std::map< mitk::Image *, mitk::ImageStatisticsCalculator::Pointer > - ImageStatisticsMapType; + void NodeRemoved(const mitk::DataNode *node); - /*! - * controls containing sliders for scrolling through the slices - */ + // member variables Ui::QmitkImageStatisticsViewControls *m_Controls; + QmitkImageStatisticsCalculationThread* m_CalculationThread; QmitkStepperAdapter* m_TimeStepperAdapter; unsigned int m_CurrentTime; - - QString m_Clipboard; + QString m_Clipboard; // Image and mask data - mitk::DataNode *m_SelectedImageNode; - mitk::Image *m_SelectedImage; - - mitk::DataNode *m_SelectedMaskNode; - mitk::Image *m_SelectedImageMask; - mitk::PlanarFigure *m_SelectedPlanarFigure; + mitk::Image* m_SelectedImage; + mitk::Image* m_SelectedImageMask; + mitk::PlanarFigure* m_SelectedPlanarFigure; + // observer tags long m_ImageObserverTag; long m_ImageMaskObserverTag; long m_PlanarFigureObserverTag; - // Hash map for associating one image statistics calculator with each iamge - // (so that previously calculated histograms / statistics can be recovered - // if a recalculation is not required) - ImageStatisticsMapType m_ImageStatisticsMap; - - mitk::ImageStatisticsCalculator::Pointer m_CurrentStatisticsCalculator; - + SelectedDataNodeVectorType m_SelectedDataNodes; + bool m_CurrentStatisticsValid; - bool m_StatisticsUpdatePending; - + bool m_StatisticsIntegrationPending; + bool m_DataNodeSelectionChanged; bool m_Visible; }; - - #endif // QmitkImageStatisticsView_H__INCLUDED 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 4c4481cf97..1d00f48e24 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui @@ -1,330 +1,339 @@ QmitkImageStatisticsViewControls true 0 0 465 - 500 + 800 Form - - - - + + + + 0 0 Mask: - + None 2 + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + Error Message + + + Qt::AutoText + + + - - - - Error Message - - - Qt::AutoText - - - - + Ignore zero-valued voxels - + Statistics 0 0 0 0 0 100 - 144 + 175 16777215 - 144 + 170 Qt::ScrollBarAlwaysOff Qt::ScrollBarAsNeeded true true true Qt::DotLine true false false 80 true 80 false true true false - 20 + 25 - 20 + 25 false false Mean StdDev RMS Max Min N V (mm³) Component 1 0 0 Copy to Clipboard Qt::Horizontal 40 20 - + 150 160 Histogram false - - - 6 - - - 0 - - - 9 - - - 0 - - - 0 - - + + 0 - - widget_2 - + - + 0 0 Copy to Clipboard Qt::Horizontal 40 20 + + + + Qt::Vertical + + + + 20 + 40 + + + + QmitkVtkHistogramWidget QWidget
QmitkVtkHistogramWidget.h
1
QmitkVtkLineProfileWidget QWidget
QmitkVtkLineProfileWidget.h
1