diff --git a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp index fb84502094..17bd264a53 100644 --- a/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.cest/src/internal/QmitkCESTStatisticsView.cpp @@ -1,805 +1,805 @@ /*=================================================================== 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. ===================================================================*/ //itk #include "itksys/SystemTools.hxx" #include <itkImageRegionConstIterator.h> #include <itkImageRegionIterator.h> // Blueberry #include <berryISelectionService.h> #include <berryIWorkbenchWindow.h> // Qmitk #include "QmitkCESTStatisticsView.h" // Qt #include <QMessageBox> #include <qclipboard.h> // qwt #include <qwt_scale_engine.h> // mitk #include <mitkCESTImageNormalizationFilter.h> #include <mitkCustomTagParser.h> #include <mitkITKImageImport.h> #include <mitkImage.h> #include <mitkImageAccessByItk.h> #include <mitkImageCast.h> #include <mitkLocaleSwitch.h> #include <mitkTemporoSpatialStringProperty.h> #include <mitkTimeGeometry.h> // boost #include <boost/tokenizer.hpp> #include <boost/algorithm/string.hpp> //stl #include <iostream> #include <string> #include <sstream> #include <algorithm> #include <iterator> #include <vector> const std::string QmitkCESTStatisticsView::VIEW_ID = "org.mitk.views.ceststatistics"; static const int STAT_TABLE_BASE_HEIGHT = 180; QmitkCESTStatisticsView::QmitkCESTStatisticsView(QObject* /*parent*/, const char* /*name*/) { this->m_CalculatorThread = new QmitkImageStatisticsCalculationThread; } QmitkCESTStatisticsView::~QmitkCESTStatisticsView() { while (this->m_CalculatorThread->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } delete this->m_CalculatorThread; } void QmitkCESTStatisticsView::SetFocus() { m_Controls.threeDimToFourDimPushButton->setFocus(); } void QmitkCESTStatisticsView::CreateQtPartControl( QWidget *parent ) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi( parent ); connect(m_Controls.threeDimToFourDimPushButton, SIGNAL(clicked()), this, SLOT(OnThreeDimToFourDimPushButtonClicked())); connect((QObject*) this->m_CalculatorThread, SIGNAL(finished()), this, SLOT(OnThreadedStatisticsCalculationEnds()), Qt::QueuedConnection); connect((QObject*)(this->m_Controls.m_CopyStatisticsToClipboardPushButton), SIGNAL(clicked()), (QObject*) this, SLOT(OnCopyStatisticsToClipboardPushButtonClicked())); connect((QObject*)(this->m_Controls.normalizeImagePushButton), SIGNAL(clicked()), (QObject*) this, SLOT(OnNormalizeImagePushButtonClicked())); connect((QObject*)(this->m_Controls.fixedRangeCheckBox), SIGNAL(toggled(bool)), (QObject*) this, SLOT(OnFixedRangeCheckBoxToggled(bool))); connect((QObject*)(this->m_Controls.fixedRangeLowerDoubleSpinBox), SIGNAL(editingFinished()), (QObject*) this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); connect((QObject*)(this->m_Controls.fixedRangeUpperDoubleSpinBox), SIGNAL(editingFinished()), (QObject*) this, SLOT(OnFixedRangeDoubleSpinBoxChanged())); m_Controls.normalizeImagePushButton->setEnabled(false); m_Controls.threeDimToFourDimPushButton->setEnabled(false); } void QmitkCESTStatisticsView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*source*/, const QList<mitk::DataNode::Pointer>& nodes ) { - if (nodes.size() == nullptr) + if (nodes.empty()) { std::stringstream message; message << "<font color='red'>Please select an image.</font>"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } // iterate all selected objects bool atLeastOneWasCESTImage = false; foreach( mitk::DataNode::Pointer node, nodes ) { if (node.IsNull()) { continue; } if( dynamic_cast<mitk::Image*>(node->GetData()) != nullptr ) { m_Controls.labelWarning->setVisible( false ); bool zSpectrumSet = SetZSpectrum(dynamic_cast<mitk::StringProperty*>(node->GetData()->GetProperty(mitk::CustomTagParser::m_OffsetsPropertyName.c_str()).GetPointer())); atLeastOneWasCESTImage = atLeastOneWasCESTImage || zSpectrumSet; if (zSpectrumSet) { m_ZImage = dynamic_cast<mitk::Image*>(node->GetData()); } else { m_MaskImage = dynamic_cast<mitk::Image*>(node->GetData()); } } if (dynamic_cast<mitk::PlanarFigure*>(node->GetData()) != nullptr) { m_MaskPlanarFigure = dynamic_cast<mitk::PlanarFigure*>(node->GetData()); } if (dynamic_cast<mitk::PointSet*>(node->GetData()) != nullptr) { m_PointSet = dynamic_cast<mitk::PointSet*>(node->GetData()); } } // We only want to offer normalization or timestep copying if one object is selected if (nodes.size() == 1) { this->Clear(); if (dynamic_cast<mitk::Image*>(nodes.front()->GetData()) ) { m_Controls.normalizeImagePushButton->setEnabled(atLeastOneWasCESTImage); m_Controls.threeDimToFourDimPushButton->setDisabled(atLeastOneWasCESTImage); } else { m_Controls.normalizeImagePushButton->setEnabled(false); m_Controls.threeDimToFourDimPushButton->setEnabled(false); std::stringstream message; message << "<font color='red'>The selected node is not an image.</font>"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } this->Clear(); return; } // we always need a mask, either image or planar figure as well as an image for further processing if (nodes.size() != 2) { this->Clear(); return; } m_Controls.normalizeImagePushButton->setEnabled(false); m_Controls.threeDimToFourDimPushButton->setEnabled(false); if (!atLeastOneWasCESTImage) { std::stringstream message; message << "<font color='red'>None of the selected data nodes contains required CEST meta information</font>"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } bool bothAreImages = (m_ZImage.GetPointer() != nullptr) && (m_MaskImage.GetPointer() != nullptr); if (bothAreImages) { bool geometriesMatch = mitk::Equal(*(m_ZImage->GetTimeGeometry()), *(m_MaskImage->GetTimeGeometry()), mitk::eps, false); if (!geometriesMatch) { std::stringstream message; message << "<font color='red'>The selected images have different geometries.</font>"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); this->Clear(); return; } } if (!this->DataSanityCheck()) { this->Clear(); return; } if (m_PointSet.IsNull()) { //// initialize thread and trigger it this->m_CalculatorThread->SetIgnoreZeroValueVoxel(false); this->m_CalculatorThread->Initialize(m_ZImage, m_MaskImage, m_MaskPlanarFigure); std::stringstream message; message << "<font color='red'>Calculating statistics...</font>"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); try { // Compute statistics this->m_CalculatorThread->start(); } catch (const mitk::Exception& e) { std::stringstream message; message << "<font color='red'>" << e.GetDescription() << "</font>"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::runtime_error &e) { // In case of exception, print error message on GUI std::stringstream message; message << "<font color='red'>" << e.what() << "</font>"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } catch (const std::exception &e) { MITK_ERROR << "Caught exception: " << e.what(); // In case of exception, print error message on GUI std::stringstream message; message << "<font color='red'>Error! Unequal Dimensions of Image and Segmentation. No recompute possible </font>"; m_Controls.labelWarning->setText(message.str().c_str()); m_Controls.labelWarning->show(); } while (this->m_CalculatorThread->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } } if (m_PointSet.IsNotNull()) { if (m_ZImage->GetDimension() == 4) { AccessFixedDimensionByItk(m_ZImage, PlotPointSet, 4); } else { MITK_WARN << "Expecting a 4D image."; } } } void QmitkCESTStatisticsView::OnThreadedStatisticsCalculationEnds() { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); const std::vector<mitk::ImageStatisticsCalculator::StatisticsContainer::Pointer> &statistics = this->m_CalculatorThread->GetStatisticsData(); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); QmitkPlotWidget::DataVector means(numberOfSpectra); QmitkPlotWidget::DataVector stdevs(numberOfSpectra); for (unsigned int index = 0; index < numberOfSpectra; ++index) { means[index] = statistics[index]->GetMean(); stdevs[index] = statistics[index]->GetStd(); } QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, means, stdevs); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve("Spectrum"); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, means, stdevs, stdevs); this->m_Controls.m_DataViewWidget->SetErrorPen(curveId, QPen(Qt::blue)); QwtSymbol* blueSymbol = new QwtSymbol(QwtSymbol::Rect, QColor(Qt::blue), QColor(Qt::blue), QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, blueSymbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); QwtLegend* legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot()->axisScaleEngine(QwtPlot::Axis::xBottom)->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } if(this->DataSanityCheck()) { this->FillStatisticsTableView(this->m_CalculatorThread->GetStatisticsData(), this->m_CalculatorThread->GetStatisticsImage()); } else { this->Clear(); } } void QmitkCESTStatisticsView::OnFixedRangeDoubleSpinBoxChanged() { if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } this->m_Controls.m_DataViewWidget->Replot(); } template <typename TPixel, unsigned int VImageDimension> void QmitkCESTStatisticsView::PlotPointSet(itk::Image<TPixel, VImageDimension>* image) { this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::xBottom, "delta w"); this->m_Controls.m_DataViewWidget->SetAxisTitle(QwtPlot::Axis::yLeft, "z"); QmitkPlotWidget::DataVector::size_type numberOfSpectra = this->m_zSpectrum.size(); auto maxIndex = this->m_PointSet->GetMaxId().Index(); for (int number = 0; number < maxIndex + 1; ++number) { mitk::PointSet::PointType point; if (!m_PointSet->GetPointIfExists(number, &point)) { continue; } itk::Index<3> itkIndex; this->m_ZImage->GetGeometry()->WorldToIndex(point, itkIndex); itk::Index<VImageDimension> itkIndexTime; itkIndexTime[0] = itkIndex[0]; itkIndexTime[1] = itkIndex[1]; itkIndexTime[2] = itkIndex[2]; QmitkPlotWidget::DataVector values(numberOfSpectra); for (int step = 0; step < numberOfSpectra; ++step) { if( VImageDimension == 4 ) { itkIndexTime[3] = step; } values[step] = image->GetPixel(itkIndexTime); } std::stringstream name; name << "Point " << number; // Qcolor enums go from 0 to 19, but 19 is transparent and 0,1 are for bitmaps // 3 is white and thus not visible QColor color(static_cast<Qt::GlobalColor>(number % 17 + 4)); QmitkPlotWidget::DataVector xValues = this->m_zSpectrum; RemoveMZeros(xValues, values); unsigned int curveId = this->m_Controls.m_DataViewWidget->InsertCurve(name.str().c_str()); this->m_Controls.m_DataViewWidget->SetCurveData(curveId, xValues, values); this->m_Controls.m_DataViewWidget->SetCurvePen(curveId, QPen(color)); QwtSymbol* symbol = new QwtSymbol(QwtSymbol::Rect, color, color, QSize(8, 8)); this->m_Controls.m_DataViewWidget->SetCurveSymbol(curveId, symbol); this->m_Controls.m_DataViewWidget->SetLegendAttribute(curveId, QwtPlotCurve::LegendShowSymbol); } if (this->m_Controls.fixedRangeCheckBox->isChecked()) { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, false); this->m_Controls.m_DataViewWidget->GetPlot()->setAxisScale(2, this->m_Controls.fixedRangeLowerDoubleSpinBox->value(), this->m_Controls.fixedRangeUpperDoubleSpinBox->value()); } else { this->m_Controls.m_DataViewWidget->GetPlot()->setAxisAutoScale(2, true); } QwtLegend* legend = new QwtLegend(); legend->setFrameShape(QFrame::Box); legend->setFrameShadow(QFrame::Sunken); legend->setLineWidth(1); this->m_Controls.m_DataViewWidget->SetLegend(legend, QwtPlot::BottomLegend); m_Controls.m_DataViewWidget->GetPlot()->axisScaleEngine(QwtPlot::Axis::xBottom)->setAttributes(QwtScaleEngine::Inverted); this->m_Controls.m_DataViewWidget->Replot(); m_Controls.labelWarning->setVisible(false); } void QmitkCESTStatisticsView::OnFixedRangeCheckBoxToggled(bool state) { this->m_Controls.fixedRangeLowerDoubleSpinBox->setEnabled(state); this->m_Controls.fixedRangeUpperDoubleSpinBox->setEnabled(state); } void QmitkCESTStatisticsView::OnNormalizeImagePushButtonClicked() { QList<mitk::DataNode::Pointer> nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode* node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information(nullptr, "CEST View", "Please load and select an image before starting image processing."); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData* data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast<mitk::Image*>(data); if (image) { std::string offsets = ""; bool hasOffsets = image->GetPropertyList()->GetStringProperty( mitk::CustomTagParser::m_OffsetsPropertyName.c_str() ,offsets); if (!hasOffsets) { QMessageBox::information(nullptr, "CEST View", "Selected image was missing CEST offset information."); return; } if (image->GetDimension() == 4) { auto normalizationFilter = mitk::CESTImageNormalizationFilter::New(); normalizationFilter->SetInput(image); normalizationFilter->Update(); auto resultImage = normalizationFilter->GetOutput(); mitk::DataNode::Pointer dataNode = mitk::DataNode::New(); dataNode->SetData(resultImage); std::string normalizedName = node->GetName() + "_normalized"; dataNode->SetName(normalizedName); this->GetDataStorage()->Add(dataNode); } this->Clear(); } } } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector& xValues, QmitkPlotWidget::DataVector& yValues) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; for (int index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); } } xValues = tempX; yValues = tempY; } void QmitkCESTStatisticsView::RemoveMZeros(QmitkPlotWidget::DataVector& xValues, QmitkPlotWidget::DataVector& yValues, QmitkPlotWidget::DataVector& stdDevs) { QmitkPlotWidget::DataVector tempX; QmitkPlotWidget::DataVector tempY; QmitkPlotWidget::DataVector tempDevs; for (int index = 0; index < xValues.size(); ++index) { if ((xValues.at(index) < -299) || (xValues.at(index)) > 299) { // do not include } else { tempX.push_back(xValues.at(index)); tempY.push_back(yValues.at(index)); tempDevs.push_back(stdDevs.at(index)); } } xValues = tempX; yValues = tempY; stdDevs = tempDevs; } void QmitkCESTStatisticsView::OnThreeDimToFourDimPushButtonClicked() { QList<mitk::DataNode::Pointer> nodes = this->GetDataManagerSelection(); if (nodes.empty()) return; mitk::DataNode* node = nodes.front(); if (!node) { // Nothing selected. Inform the user and return QMessageBox::information( nullptr, "CEST View", "Please load and select an image before starting image processing."); return; } // here we have a valid mitk::DataNode // a node itself is not very useful, we need its data item (the image) mitk::BaseData* data = node->GetData(); if (data) { // test if this data item is an image or not (could also be a surface or something totally different) mitk::Image* image = dynamic_cast<mitk::Image*>( data ); if (image) { if (image->GetDimension() == 4) { AccessFixedDimensionByItk(image, CopyTimesteps, 4); } this->Clear(); } } } template <typename TPixel, unsigned int VImageDimension> void QmitkCESTStatisticsView::CopyTimesteps(itk::Image<TPixel, VImageDimension>* image) { typedef itk::Image<TPixel, VImageDimension> ImageType; //typedef itk::PasteImageFilter<ImageType, ImageType> PasteImageFilterType; unsigned int numberOfTimesteps = image->GetLargestPossibleRegion().GetSize(3); typename ImageType::RegionType sourceRegion = image->GetLargestPossibleRegion(); sourceRegion.SetSize(3, 1); typename ImageType::RegionType targetRegion = image->GetLargestPossibleRegion(); targetRegion.SetSize(3, 1); for (unsigned int timestep = 1; timestep < numberOfTimesteps; ++timestep) { targetRegion.SetIndex(3, timestep); itk::ImageRegionConstIterator<ImageType> sourceIterator(image, sourceRegion); itk::ImageRegionIterator<ImageType> targetIterator(image, targetRegion); while (!sourceIterator.IsAtEnd()) { targetIterator.Set(sourceIterator.Get()); ++sourceIterator; ++targetIterator; } } } bool QmitkCESTStatisticsView::SetZSpectrum(mitk::StringProperty* zSpectrumProperty) { if (zSpectrumProperty == nullptr) { return false; } mitk::LocaleSwitch localeSwitch("C"); std::string zSpectrumString = zSpectrumProperty->GetValueAsString(); std::istringstream iss(zSpectrumString); std::vector<std::string> zSpectra; std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), std::back_inserter(zSpectra)); m_zSpectrum.clear(); m_zSpectrum.resize(0); for (auto const &spectrumString : zSpectra) { m_zSpectrum.push_back(std::stod(spectrumString)); } return (m_zSpectrum.size() > 0); } void QmitkCESTStatisticsView::FillStatisticsTableView( const std::vector<mitk::ImageStatisticsCalculator::StatisticsContainer::Pointer> &s, const mitk::Image *image) { this->m_Controls.m_StatisticsTable->setColumnCount(image->GetTimeSteps()); this->m_Controls.m_StatisticsTable->horizontalHeader()->setVisible(image->GetTimeSteps() > 1); int decimals = 2; mitk::PixelType doublePix = mitk::MakeScalarPixelType< double >(); mitk::PixelType floatPix = mitk::MakeScalarPixelType< float >(); if (image->GetPixelType() == doublePix || image->GetPixelType() == floatPix) { decimals = 5; } for (unsigned int t = 0; t < image->GetTimeSteps(); t++) { this->m_Controls.m_StatisticsTable->setHorizontalHeaderItem(t, new QTableWidgetItem(QString::number(m_zSpectrum[t]))); this->m_Controls.m_StatisticsTable->setItem(0, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetMean(), 0, 'f', decimals))); this->m_Controls.m_StatisticsTable->setItem(1, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetStd(), 0, 'f', decimals))); this->m_Controls.m_StatisticsTable->setItem(2, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetRMS(), 0, 'f', decimals))); QString max; max.append(QString("%1").arg(s[t]->GetMax(), 0, 'f', decimals)); max += " ("; for (unsigned int i = 0; i<s[t]->GetMaxIndex().size(); i++) { max += QString::number(s[t]->GetMaxIndex()[i]); if (i<s[t]->GetMaxIndex().size() - 1) max += ","; } max += ")"; this->m_Controls.m_StatisticsTable->setItem(3, t, new QTableWidgetItem(max)); QString min; min.append(QString("%1").arg(s[t]->GetMin(), 0, 'f', decimals)); min += " ("; for (unsigned int i = 0; i<s[t]->GetMinIndex().size(); i++) { min += QString::number(s[t]->GetMinIndex()[i]); if (i<s[t]->GetMinIndex().size() - 1) min += ","; } min += ")"; this->m_Controls.m_StatisticsTable->setItem(4, t, new QTableWidgetItem(min)); this->m_Controls.m_StatisticsTable->setItem(5, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetN()))); const mitk::BaseGeometry *geometry = image->GetGeometry(); if (geometry != nullptr) { const mitk::Vector3D &spacing = image->GetGeometry()->GetSpacing(); double volume = spacing[0] * spacing[1] * spacing[2] * (double)s[t]->GetN(); this->m_Controls.m_StatisticsTable->setItem(6, t, new QTableWidgetItem( QString("%1").arg(volume, 0, 'f', decimals))); } else { this->m_Controls.m_StatisticsTable->setItem(6, t, new QTableWidgetItem( "NA")); } } this->m_Controls.m_StatisticsTable->resizeColumnsToContents(); int height = STAT_TABLE_BASE_HEIGHT; if (this->m_Controls.m_StatisticsTable->horizontalHeader()->isVisible()) height += this->m_Controls.m_StatisticsTable->horizontalHeader()->height(); //if (this->m_Controls.m_StatisticsTable->horizontalScrollBar()->isVisible()) // height += this->m_Controls.m_StatisticsTable->horizontalScrollBar()->height(); this->m_Controls.m_StatisticsTable->setMinimumHeight(height); this->m_Controls.m_StatisticsGroupBox->setEnabled(true); this->m_Controls.m_StatisticsTable->setEnabled(true); } void QmitkCESTStatisticsView::InvalidateStatisticsTableView() { this->m_Controls.m_StatisticsTable->horizontalHeader()->setVisible(false); this->m_Controls.m_StatisticsTable->setColumnCount(1); for (int i = 0; i < this->m_Controls.m_StatisticsTable->rowCount(); ++i) { { this->m_Controls.m_StatisticsTable->setItem(i, 0, new QTableWidgetItem("NA")); } } this->m_Controls.m_StatisticsTable->setMinimumHeight(STAT_TABLE_BASE_HEIGHT); this->m_Controls.m_StatisticsTable->setEnabled(false); } bool QmitkCESTStatisticsView::DataSanityCheck() { QmitkPlotWidget::DataVector::size_type numberOfSpectra = m_zSpectrum.size(); // if we do not have a spectrum, the data can not be processed if (numberOfSpectra == 0) { return false; } // if we do not have CEST image data, the data can not be processed if (m_ZImage.IsNull()) { return false; } // if the CEST image data and the meta information do not match, the data can not be processed if (numberOfSpectra != m_ZImage->GetTimeSteps()) { return false; } // if we have neither a mask image, a point set nor a mask planar figure, we can not do statistics // statistics on the whole image would not make sense if (m_MaskImage.IsNull() && m_MaskPlanarFigure.IsNull() && m_PointSet.IsNull() ) { return false; } // if we have a mask image and a mask planar figure, we can not do statistics // we do not know which one to use if (m_MaskImage.IsNotNull() && m_MaskPlanarFigure.IsNotNull()) { return false; } return true; } void QmitkCESTStatisticsView::Clear() { this->m_zSpectrum.clear(); this->m_zSpectrum.resize(0); this->m_ZImage = nullptr; this->m_MaskImage = nullptr; this->m_MaskPlanarFigure = nullptr; this->m_PointSet = nullptr; this->m_Controls.m_DataViewWidget->Clear(); this->InvalidateStatisticsTableView(); this->m_Controls.m_StatisticsGroupBox->setEnabled(false); } void QmitkCESTStatisticsView::OnCopyStatisticsToClipboardPushButtonClicked() { QLocale tempLocal; QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); const std::vector<mitk::ImageStatisticsCalculator::StatisticsContainer::Pointer> &statistics = this->m_CalculatorThread->GetStatisticsData(); QmitkPlotWidget::DataVector::size_type size = m_zSpectrum.size(); QString clipboard("delta_w \t Mean \t StdDev \t RMS \t Max \t Min \t N\n"); for (QmitkPlotWidget::DataVector::size_type index = 0; index < size; ++index) { // Copy statistics to clipboard ("%Ln" will use the default locale for // number formatting) clipboard = clipboard.append("%L1 \t %L2 \t %L3 \t %L4 \t %L5 \t %L6 \t %L7\n") .arg(m_zSpectrum[index], 0, 'f', 10) .arg(statistics[index]->GetMean(), 0, 'f', 10) .arg(statistics[index]->GetStd(), 0, 'f', 10) .arg(statistics[index]->GetRMS(), 0, 'f', 10) .arg(statistics[index]->GetMax(), 0, 'f', 10) .arg(statistics[index]->GetMin(), 0, 'f', 10) .arg(statistics[index]->GetN()); } QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard); QLocale::setDefault(tempLocal); } 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 9bd72e7454..010d9c3fbe 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -1,1361 +1,1361 @@ /*=================================================================== 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" // Qt includes #include <qclipboard.h> #include <qscrollbar.h> #include <QVector> // berry includes #include <berryIWorkbenchPage.h> // mitk includes #include "mitkNodePredicateDataType.h" #include "mitkNodePredicateOr.h" #include "mitkPlanarFigureInteractor.h" // itk includes #include "itksys/SystemTools.hxx" #include <mitkILinkedRenderWindowPart.h> #include <QmitkRenderWindow.h> #include <limits> const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics"; const int QmitkImageStatisticsView::STAT_TABLE_BASE_HEIGHT = 180; QmitkImageStatisticsView::QmitkImageStatisticsView(QObject* /*parent*/, const char* /*name*/) : m_Controls( nullptr ), m_TimeStepperAdapter( nullptr ), m_SelectedImage( nullptr ), m_SelectedImageMask( nullptr ), m_SelectedPlanarFigure( nullptr ), m_ImageObserverTag( -1 ), m_ImageMaskObserverTag( -1 ), m_PlanarFigureObserverTag( -1 ), m_TimeObserverTag( -1 ), m_CurrentStatisticsValid( false ), m_StatisticsUpdatePending( false ), m_DataNodeSelectionChanged ( false ), m_Visible(false) { this->m_CalculationThread = new QmitkImageStatisticsCalculationThread; } QmitkImageStatisticsView::~QmitkImageStatisticsView() { if ( m_SelectedImage != nullptr ) m_SelectedImage->RemoveObserver( m_ImageObserverTag ); if ( m_SelectedImageMask != nullptr ) m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); if ( m_SelectedPlanarFigure != nullptr ) 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 == nullptr) { m_Controls = new Ui::QmitkImageStatisticsViewControls; m_Controls->setupUi(parent); CreateConnections(); m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_BinSizeFrame->setVisible(false); } } void QmitkImageStatisticsView::CreateConnections() { if ( m_Controls ) { 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); connect( (QObject*) this->m_Controls->m_StatisticsTable, SIGNAL(cellDoubleClicked(int,int)),this, SLOT( JumpToCoordinates(int,int)) ); connect((QObject*)(this->m_Controls->m_barRadioButton), SIGNAL(clicked()), (QObject*)(this), SLOT(OnBarRadioButtonSelected())); connect((QObject*)(this->m_Controls->m_lineRadioButton), SIGNAL(clicked()), (QObject*)(this), SLOT(OnLineRadioButtonSelected())); connect( (QObject*) (this->m_Controls->m_HistogramBinSizeSpinbox), SIGNAL(editingFinished()), this, SLOT(OnHistogramBinSizeBoxValueChanged())); connect((QObject*)(this->m_Controls->m_UseDefaultBinSizeBox), SIGNAL(clicked()), (QObject*) this, SLOT(OnDefaultBinSizeBoxChanged())); connect((QObject*)(this->m_Controls->m_ShowSubchartCheckBox), SIGNAL(clicked()), (QObject*) this, SLOT(OnShowSubchartBoxChanged())); } } void QmitkImageStatisticsView::OnDefaultBinSizeBoxChanged() { m_Controls->m_BinSizeFrame->setVisible(!m_Controls->m_UseDefaultBinSizeBox->isChecked()); if (m_CalculationThread != nullptr){ m_Controls->m_HistogramBinSizeSpinbox->setValue(m_CalculationThread->GetHistogramBinSize()); m_CalculationThread->SetUseDefaultNBins(m_Controls->m_UseDefaultBinSizeBox->isChecked()); } this->UpdateStatistics(); } void QmitkImageStatisticsView::OnShowSubchartBoxChanged() { this->m_Controls->m_JSHistogram->SetAppearance( this->m_Controls->m_lineRadioButton->isChecked(), this->m_Controls->m_ShowSubchartCheckBox->isChecked()); QString useLineChart = "false"; if (this->m_Controls->m_lineRadioButton->isChecked()) useLineChart = "true"; QString showSubchart = "false"; if (this->m_Controls->m_ShowSubchartCheckBox->isChecked()) showSubchart = "true"; this->m_Controls->m_JSHistogram->SendCommand( "ReloadChart(" + useLineChart + "," + showSubchart + ")"); } void QmitkImageStatisticsView::OnBarRadioButtonSelected() { this->m_Controls->m_JSHistogram->TransformView("bar"); } void QmitkImageStatisticsView::OnLineRadioButtonSelected() { this->m_Controls->m_JSHistogram->TransformView("line"); } void QmitkImageStatisticsView::PartClosed(const berry::IWorkbenchPartReference::Pointer& ) { } void QmitkImageStatisticsView::OnTimeChanged(const itk::EventObject& e) { if (this->m_SelectedDataNodes.isEmpty() || this->m_SelectedImage == nullptr) return; const mitk::SliceNavigationController::GeometryTimeEvent* timeEvent = dynamic_cast<const mitk::SliceNavigationController::GeometryTimeEvent*>(&e); assert(timeEvent != nullptr); unsigned int timestep = timeEvent->GetPos(); if (this->m_SelectedImage->GetTimeSteps() > 1) { for (int x = 0; x < this->m_Controls->m_StatisticsTable->columnCount(); x++) { for (int y = 0; y < this->m_Controls->m_StatisticsTable->rowCount(); y++) { QTableWidgetItem* item = this->m_Controls->m_StatisticsTable->item(y, x); if (item == nullptr) break; if (x == timestep) { item->setBackgroundColor(Qt::yellow); } else { if (y % 2 == 0) item->setBackground(this->m_Controls->m_StatisticsTable->palette().base()); else item->setBackground(this->m_Controls->m_StatisticsTable->palette().alternateBase()); } } } this->m_Controls->m_StatisticsTable->viewport()->update(); } if ((this->m_SelectedImage->GetTimeSteps() == 1 && timestep == 0) || this->m_SelectedImage->GetTimeSteps() > 1) { // display histogram for selected timestep this->m_Controls->m_JSHistogram->ClearHistogram(); QmitkImageStatisticsCalculationThread::HistogramType::Pointer histogram = this->m_CalculationThread->GetTimeStepHistogram(timestep); if (histogram.IsNotNull()) { bool closedFigure = this->m_CalculationThread->GetStatisticsUpdateSuccessFlag(); if ( closedFigure ) { this->m_Controls->m_JSHistogram->ComputeHistogram( histogram.GetPointer(), this->m_Controls->m_lineRadioButton->isChecked(), this->m_Controls->m_ShowSubchartCheckBox->isChecked() ); } //this->m_Controls->m_JSHistogram->ComputeHistogram(histogram.GetPointer()); /*else { m_Controls->m_JSHistogram->ComputeIntensityProfile(timestep, true); }*/ // this->m_Controls->m_JSHistogram->SignalGraphChanged(); // hacky way to make sure the protected SignalGraphChanged() is called //if (this->m_Controls->m_JSHistogram->GetUseLineGraph()) //{ //this->m_Controls->m_JSHistogram->OnBarRadioButtonSelected(); //this->m_Controls->m_JSHistogram->OnLineRadioButtonSelected(); //} //else //{ //this->m_Controls->m_JSHistogram->OnLineRadioButtonSelected(); //this->m_Controls->m_JSHistogram->OnBarRadioButtonSelected(); //} } } } void QmitkImageStatisticsView::JumpToCoordinates(int row ,int col) { if(m_SelectedDataNodes.isEmpty()) { MITK_WARN("QmitkImageStatisticsView") << "No data node selected for statistics calculation." ; return; } mitk::Point3D world; if (row==5 && !m_WorldMinList.empty()) world = m_WorldMinList[col]; else if (row==4 && !m_WorldMaxList.empty()) world = m_WorldMaxList[col]; else return; mitk::IRenderWindowPart* part = this->GetRenderWindowPart(); if (part) { part->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SelectSliceByPoint(world); part->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()->SelectSliceByPoint(world); part->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()->SelectSliceByPoint(world); mitk::SliceNavigationController::GeometryTimeEvent timeEvent(this->m_SelectedImage->GetTimeGeometry(), col); part->GetQmitkRenderWindow("axial")->GetSliceNavigationController()->SetGeometryTime(timeEvent); } } void QmitkImageStatisticsView::OnIgnoreZerosCheckboxClicked() { emit StatisticsUpdate(); } void QmitkImageStatisticsView::OnClipboardHistogramButtonClicked() { if ( m_CurrentStatisticsValid && !( m_SelectedPlanarFigure != nullptr)) { const unsigned int t = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos(); typedef mitk::ImageStatisticsCalculator::HistogramType HistogramType; const HistogramType *histogram = this->m_CalculationThread->GetTimeStepHistogram(t).GetPointer(); QString clipboard( "Measurement \t Frequency\n" ); for ( HistogramType::ConstIterator it = histogram->Begin(); it != histogram->End(); ++it ) { if( m_Controls->m_HistogramBinSizeSpinbox->value() == 1.0) { clipboard = clipboard.append( "%L1 \t %L2\n" ) .arg( it.GetMeasurementVector()[0], 0, 'f', 0 ) .arg( it.GetFrequency() ); } else { clipboard = clipboard.append( "%L1 \t %L2\n" ) .arg( it.GetMeasurementVector()[0], 0, 'f', 2 ) .arg( it.GetFrequency() ); } } QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); } // If a (non-closed) PlanarFigure is selected, display a line profile widget else if ( m_CurrentStatisticsValid && (m_SelectedPlanarFigure != nullptr )) { /*auto intensity = m_Controls->m_JSHistogram->GetFrequency(); auto pixel = m_Controls->m_JSHistogram->GetMeasurement(); QString clipboard( "Pixel \t Intensity\n" ); auto j = pixel.begin(); for (auto i = intensity.begin(); i < intensity.end(); i++) { assert(j != pixel.end()); clipboard = clipboard.append( "%L1 \t %L2\n" ) .arg( (*j).toString()) .arg( (*i).toString()); j++; } QApplication::clipboard()->setText( clipboard, QClipboard::Clipboard ); */ } else { QApplication::clipboard()->clear(); } } void QmitkImageStatisticsView::OnClipboardStatisticsButtonClicked() { QLocale tempLocal; QLocale::setDefault(QLocale(QLocale::English, QLocale::UnitedStates)); if ( m_CurrentStatisticsValid && !( m_SelectedPlanarFigure != nullptr)) { const std::vector<mitk::ImageStatisticsCalculator::StatisticsContainer::Pointer> &statistics = this->m_CalculationThread->GetStatisticsData(); // Set time borders for for loop ;) unsigned int startT, endT; if(this->m_Controls->m_CheckBox4dCompleteTable->checkState()==Qt::CheckState::Unchecked) { startT = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()-> GetPos(); endT = startT+1; } else { startT = 0; endT = statistics.size(); } QVector< QVector<QString> > statisticsTable; QStringList headline; // Create Headline headline << " " << "Mean" << "Median" << "StdDev" << "RMS" << "Max" << "Min" << "NumberOfVoxels" << "Skewness" << "Kurtosis" << "Uniformity" << "Entropy" << "MPP" << "UPP" << "V [mm³]"; for(int i=0;i<headline.size();i++) { QVector<QString> row; row.append(headline.at(i)); statisticsTable.append(row); } // Fill Table for(unsigned int t=startT;t<endT;t++) { // Copy statistics to clipboard ("%Ln" will use the default locale for // number formatting) QStringList value; value << QString::number(t) << QString::number(statistics[t]->GetMean()) << QString::number(statistics[t]->GetMedian()) << QString::number(statistics[t]->GetStd()) << QString::number(statistics[t]->GetRMS()) << QString::number(statistics[t]->GetMax()) << QString::number(statistics[t]->GetMin()) << QString::number(statistics[t]->GetN()) << QString::number(statistics[t]->GetSkewness()) << QString::number(statistics[t]->GetKurtosis()) << QString::number(statistics[t]->GetUniformity()) << QString::number(statistics[t]->GetEntropy()) << QString::number(statistics[t]->GetMPP()) << QString::number(statistics[t]->GetUPP()) << QString::number(m_Controls->m_StatisticsTable->item(7, 0)->data(Qt::DisplayRole).toDouble()); for(int z=0;z<value.size();z++) { statisticsTable[z].append(value.at(z)); } } // Create output string QString clipboard; for(int i=0;i<statisticsTable.size();i++) { for(int t=0;t<statisticsTable.at(i).size();t++) { clipboard.append(statisticsTable.at(i).at(t)); clipboard.append("\t"); } clipboard.append("\n"); } QApplication::clipboard()->setText(clipboard, QClipboard::Clipboard); } else { QApplication::clipboard()->clear(); } QLocale::setDefault(tempLocal); } void QmitkImageStatisticsView::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*part*/, const QList<mitk::DataNode::Pointer> &nodes ) { if (this->m_Visible) { this->SelectionChanged( nodes ); } else { this->m_DataNodeSelectionChanged = true; } } void QmitkImageStatisticsView::SelectionChanged(const QList<mitk::DataNode::Pointer> &selectedNodes) { if( this->m_StatisticsUpdatePending ) { this->m_DataNodeSelectionChanged = true; return; // not ready for new data now! } if (selectedNodes.size() == this->m_SelectedDataNodes.size()) { int i = 0; for (; i < selectedNodes.size(); ++i) { if (selectedNodes.at(i) != this->m_SelectedDataNodes.at(i)) { break; } } // node selection did not change if (i == selectedNodes.size()) return; } //reset the feature image and image mask field m_Controls->m_SelectedFeatureImageLabel->setText("None"); m_Controls->m_SelectedMaskLabel->setText("None"); this->ReinitData(); if (selectedNodes.isEmpty()) { m_Controls->m_JSHistogram->ClearHistogram(); m_Controls->m_lineRadioButton->setEnabled(true); m_Controls->m_barRadioButton->setEnabled(true); m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); // m_Controls->m_HistogramBinSizeLabel->setEnabled(true); m_Controls->m_InfoLabel->setText(QString("")); // m_Controls->horizontalLayout_3->setEnabled(false); m_Controls->groupBox->setEnabled(false); m_Controls->groupBox_3->setEnabled(false); } else { // m_Controls->horizontalLayout_3->setEnabled(true); m_Controls->groupBox->setEnabled(true); m_Controls->groupBox_3->setEnabled(true); } if(selectedNodes.size() == 1 || selectedNodes.size() == 2) { bool isBinary = false; selectedNodes.value(0)->GetBoolProperty("binary",isBinary); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); isBinary |= isLabelSet->CheckNode(selectedNodes.value(0)); if(isBinary) { m_Controls->m_JSHistogram->ClearHistogram(); m_Controls->m_lineRadioButton->setEnabled(true); m_Controls->m_barRadioButton->setEnabled(true); m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); // m_Controls->m_HistogramBinSizeLabel->setEnabled(true); m_Controls->m_InfoLabel->setText(QString("")); } 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(); } else { this->m_DataNodeSelectionChanged = false; } } void QmitkImageStatisticsView::ReinitData() { while( this->m_CalculationThread->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } if(this->m_SelectedImage != nullptr) { this->m_SelectedImage->RemoveObserver( this->m_ImageObserverTag); this->m_SelectedImage = nullptr; } if(this->m_SelectedImageMask != nullptr) { this->m_SelectedImageMask->RemoveObserver( this->m_ImageMaskObserverTag); this->m_SelectedImageMask = nullptr; } if(this->m_SelectedPlanarFigure != nullptr) { this->m_SelectedPlanarFigure->RemoveObserver( this->m_PlanarFigureObserverTag); this->m_SelectedPlanarFigure = nullptr; } this->m_SelectedDataNodes.clear(); this->m_StatisticsUpdatePending = false; m_Controls->m_ErrorMessageLabel->setText( "" ); m_Controls->m_ErrorMessageLabel->hide(); this->InvalidateStatisticsTableView(); m_Controls->m_JSHistogram->ClearHistogram(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); } void QmitkImageStatisticsView::OnThreadedStatisticsCalculationEnds() { std::stringstream message; message << ""; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->hide(); this->WriteStatisticsToGUI(); } void QmitkImageStatisticsView::UpdateStatistics() { mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); if ( renderPart == nullptr ) { this->m_StatisticsUpdatePending = false; return; } m_WorldMinList.clear(); m_WorldMaxList.clear(); // classify selected nodes mitk::NodePredicateDataType::Pointer isImage = mitk::NodePredicateDataType::New("Image"); mitk::NodePredicateDataType::Pointer isLabelSet = mitk::NodePredicateDataType::New("LabelSetImage"); mitk::NodePredicateOr::Pointer imagePredicate = mitk::NodePredicateOr::New(isImage, isLabelSet); std::string maskName = std::string(); std::string maskType = std::string(); std::string featureImageName = std::string(); unsigned int maskDimension = 0; // reset data from last run ITKCommandType::Pointer changeListener = ITKCommandType::New(); changeListener->SetCallbackFunction( this, &QmitkImageStatisticsView::SelectedDataModified ); mitk::DataNode::Pointer planarFigureNode; for( int i= 0 ; i < this->m_SelectedDataNodes.size(); ++i) { mitk::PlanarFigure::Pointer planarFig = dynamic_cast<mitk::PlanarFigure*>(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); isMask |= isLabelSet->CheckNode(this->m_SelectedDataNodes.at(i)); if( this->m_SelectedImageMask == nullptr && isMask) { this->m_SelectedImageMask = dynamic_cast<mitk::Image*>(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( !isMask ) { if(this->m_SelectedImage == nullptr) { this->m_SelectedImage = static_cast<mitk::Image*>(this->m_SelectedDataNodes.at(i)->GetData()); this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener); } featureImageName = this->m_SelectedDataNodes.at(i)->GetName(); } } else if (planarFig.IsNotNull()) { if(this->m_SelectedPlanarFigure == nullptr) { 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; planarFigureNode = m_SelectedDataNodes.at(i); } } else { std::stringstream message; message << "<font color='red'>" << "Invalid data node type!" << "</font>"; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); } } if(maskName == "") { maskName = "None"; maskType = ""; maskDimension = 0; } if(featureImageName == "") { featureImageName = "None"; } if (m_SelectedPlanarFigure != nullptr && m_SelectedImage == nullptr) { mitk::DataStorage::SetOfObjects::ConstPointer parentSet = this->GetDataStorage()->GetSources(planarFigureNode); for (int i=0; i<parentSet->Size(); i++) { mitk::DataNode::Pointer node = parentSet->ElementAt(i); if( imagePredicate->CheckNode(node) ) { bool isMask = false; node->GetPropertyValue("binary", isMask); isMask |= isLabelSet->CheckNode(node); if( !isMask ) { if(this->m_SelectedImage == nullptr) { this->m_SelectedImage = static_cast<mitk::Image*>(node->GetData()); this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener); } } } } } unsigned int timeStep = renderPart->GetTimeNavigationController()->GetTime()->GetPos(); if ( m_SelectedImage != nullptr && 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 << "<font color='red'>Multi-component images not supported.</font>"; 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_JSHistogram->ClearHistogram(); m_CurrentStatisticsValid = false; this->m_StatisticsUpdatePending = false; m_Controls->m_lineRadioButton->setEnabled(true); m_Controls->m_barRadioButton->setEnabled(true); m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); // m_Controls->m_HistogramBinSizeLabel->setEnabled(true); m_Controls->m_InfoLabel->setText(QString("")); return; } std::stringstream maskLabel; maskLabel << maskName; if ( maskDimension > 0 ) { maskLabel << " [" << maskDimension << "D " << maskType << "]"; } m_Controls->m_SelectedMaskLabel->setText( maskLabel.str().c_str() ); m_Controls->m_SelectedFeatureImageLabel->setText(featureImageName.c_str()); // check time step validity if(m_SelectedImage->GetDimension() <= 3 && timeStep > m_SelectedImage->GetDimension(3)-1) { timeStep = m_SelectedImage->GetDimension(3)-1; } // Add the used mask time step to the mask label so the user knows which mask time step was used // if the image time step is bigger than the total number of mask time steps (see // ImageStatisticsCalculator::ExtractImageAndMask) if (m_SelectedImageMask != nullptr) { unsigned int maskTimeStep = timeStep; if (maskTimeStep >= m_SelectedImageMask->GetTimeSteps()) { maskTimeStep = m_SelectedImageMask->GetTimeSteps() - 1; } m_Controls->m_SelectedMaskLabel->setText(m_Controls->m_SelectedMaskLabel->text() + QString(" (t=") + QString::number(maskTimeStep) + QString(")")); } //// 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 << "<font color='red'>Calculating statistics...</font>"; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); try { // Compute statistics // this->m_CalculationThread->SetUseDefaultBinSize(m_Controls->m_UseDefaultBinSizeBox->isChecked()); this->m_CalculationThread->start(); } catch ( const mitk::Exception& e) { std::stringstream message; message << "<font color='red'>" << e.GetDescription() << "</font>"; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); this->m_StatisticsUpdatePending = false; } catch ( const std::runtime_error &e ) { // In case of exception, print error message on GUI std::stringstream message; message << "<font color='red'>" << e.what() << "</font>"; 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 << "<font color='red'>Error! Unequal Dimensions of Image and Segmentation. No recompute possible </font>"; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); this->m_StatisticsUpdatePending = false; } } else { this->m_StatisticsUpdatePending = false; } } void QmitkImageStatisticsView::SelectedDataModified() { if( !m_StatisticsUpdatePending ) { emit StatisticsUpdate(); } } void QmitkImageStatisticsView::NodeRemoved(const mitk::DataNode *node) { while(this->m_CalculationThread->isRunning()) // wait until thread has finished { itksys::SystemTools::Delay(100); } if (node->GetData() == m_SelectedImage) { m_SelectedImage = nullptr; } } void QmitkImageStatisticsView::RequestStatisticsUpdate() { if ( !m_StatisticsUpdatePending ) { if(this->m_DataNodeSelectionChanged) { this->SelectionChanged(this->GetCurrentSelection()); } else { this->m_StatisticsUpdatePending = true; this->UpdateStatistics(); } } if (this->GetRenderWindowPart()) this->GetRenderWindowPart()->RequestUpdate(); } void QmitkImageStatisticsView::OnHistogramBinSizeBoxValueChanged() { if (m_Controls->m_HistogramBinSizeSpinbox->value() != m_HistogramBinSize) { m_HistogramBinSize = m_Controls->m_HistogramBinSizeSpinbox->value(); this->m_CalculationThread->SetHistogramBinSize(m_Controls->m_HistogramBinSizeSpinbox->value()); this->UpdateStatistics(); } } void QmitkImageStatisticsView::WriteStatisticsToGUI() { disconnect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), 0, 0); m_Controls->m_lineRadioButton->setEnabled(true); m_Controls->m_barRadioButton->setEnabled(true); m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); // m_Controls->m_HistogramBinSizeLabel->setEnabled(true); m_Controls->m_InfoLabel->setText(QString("")); if(m_DataNodeSelectionChanged) { this->m_StatisticsUpdatePending = false; this->RequestStatisticsUpdate(); return; // stop visualization of results and calculate statistics of new selection } if ( this->m_CalculationThread->GetStatisticsUpdateSuccessFlag()) { if ( this->m_CalculationThread->GetStatisticsChangedFlag() ) { // Do not show any error messages m_Controls->m_ErrorMessageLabel->hide(); m_CurrentStatisticsValid = true; } if (m_Controls->m_barRadioButton->isChecked()) { //m_Controls->m_JSHistogram->OnBarRadioButtonSelected(); } m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramBinSizeSpinbox->setValue( this->m_CalculationThread->GetHistogramBinSize() ); //m_Controls->m_JSHistogram->ComputeHistogram( this->m_CalculationThread->GetTimeStepHistogram(this->m_CalculationThread->GetTimeStep()).GetPointer() ); this->FillStatisticsTableView( this->m_CalculationThread->GetStatisticsData(), this->m_CalculationThread->GetStatisticsImage()); m_CurrentStatisticsValid = true; } else { m_Controls->m_SelectedMaskLabel->setText( "None" ); m_Controls->m_ErrorMessageLabel->setText( m_CalculationThread->GetLastErrorMessage().c_str() ); m_Controls->m_ErrorMessageLabel->show(); // Clear statistics and histogram this->InvalidateStatisticsTableView(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); //m_Controls->m_JSHistogram->clearHistogram(); m_CurrentStatisticsValid = false; // If a (non-closed) PlanarFigure is selected, display a line profile widget if ( m_SelectedPlanarFigure != nullptr ) { // Check if the (closed) planar figure is out of bounds and so no image mask could be calculated--> Intensity Profile can not be calculated bool outOfBounds = false; if ( m_SelectedPlanarFigure->IsClosed() && m_SelectedImageMask == nullptr) { outOfBounds = true; std::stringstream message; message << "<font color='red'>Planar figure is on a rotated image plane or outside the image bounds.</font>"; m_Controls->m_InfoLabel->setText(message.str().c_str()); } // check whether PlanarFigure is initialized const mitk::PlaneGeometry *planarFigurePlaneGeometry = m_SelectedPlanarFigure->GetPlaneGeometry(); if ( !(planarFigurePlaneGeometry == nullptr || outOfBounds)) { unsigned int timeStep = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()->GetPos(); m_Controls->m_JSHistogram->SetImage(this->m_CalculationThread->GetStatisticsImage()); m_Controls->m_JSHistogram->SetPlanarFigure(m_SelectedPlanarFigure); connect((QObject*)(this->m_Controls->m_JSHistogram), SIGNAL(PageSuccessfullyLoaded()), (QObject*) this, SLOT(OnLineRadioButtonSelected())); m_Controls->m_JSHistogram->ComputeIntensityProfile(timeStep, true); //m_Controls->m_JSHistogram->ComputeIntensityProfile(timeStep); //this->ComputeIntensityProfile(m_SelectedPlanarFigure, this->m_CalculationThread->GetStatisticsImage(), timeStep, true); m_Controls->m_lineRadioButton->setChecked(true); m_Controls->m_lineRadioButton->setEnabled(false); m_Controls->m_barRadioButton->setEnabled(false); m_Controls->m_HistogramBinSizeSpinbox->setEnabled(false); m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(false); // m_Controls->m_HistogramBinSizeLabel->setEnabled(false); this->FillLinearProfileStatisticsTableView( this->m_CalculationThread->GetStatisticsImage() ); std::stringstream message; message << "<font color='red'>Only linegraph available for an intensity profile!</font>"; m_Controls->m_InfoLabel->setText(message.str().c_str()); m_CurrentStatisticsValid = true; } else { // Clear statistics, histogram, and GUI this->InvalidateStatisticsTableView(); m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_JSHistogram->ClearHistogram(); m_CurrentStatisticsValid = false; m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_SelectedMaskLabel->setText( "None" ); this->m_StatisticsUpdatePending = false; m_Controls->m_lineRadioButton->setEnabled(true); m_Controls->m_barRadioButton->setEnabled(true); m_Controls->m_HistogramBinSizeSpinbox->setEnabled(true); m_Controls->m_HistogramBinSizeCaptionLabel->setEnabled(true); // m_Controls->m_HistogramBinSizeLabel->setEnabled(true); if (!outOfBounds) m_Controls->m_InfoLabel->setText(QString("")); return; // Sebastian Wirkert: would suggest to remove this return, since it is an artifact of previous // code architecture. However, removing it will cause m_StatisticsUpdatePending to be set to false // in case of invalid statistics which it previously was not. } } } this->m_StatisticsUpdatePending = false; } void QmitkImageStatisticsView::FillStatisticsTableView( const std::vector<mitk::ImageStatisticsCalculator::StatisticsContainer::Pointer> &s, const mitk::Image *image ) { this->m_Controls->m_StatisticsTable->setColumnCount(image->GetTimeSteps()); this->m_Controls->m_StatisticsTable->horizontalHeader()->setVisible(image->GetTimeSteps() > 1); // Set Checkbox for complete copy of statistic table if(image->GetTimeSteps()>1) { this->m_Controls->m_CheckBox4dCompleteTable->setEnabled(true); } else { this->m_Controls->m_CheckBox4dCompleteTable->setEnabled(false); this->m_Controls->m_CheckBox4dCompleteTable->setChecked(false); } int decimals = 2; mitk::PixelType doublePix = mitk::MakeScalarPixelType< double >(); mitk::PixelType floatPix = mitk::MakeScalarPixelType< float >(); if (image->GetPixelType()==doublePix || image->GetPixelType()==floatPix) { decimals = 5; } for (unsigned int t = 0; t < image->GetTimeSteps(); t++) { this->m_Controls->m_StatisticsTable->setHorizontalHeaderItem(t, new QTableWidgetItem(QString::number(t))); if (s[t]->GetMaxIndex().size()==3) { mitk::Point3D index, max, min; index[0] = s[t]->GetMaxIndex()[0]; index[1] = s[t]->GetMaxIndex()[1]; index[2] = s[t]->GetMaxIndex()[2]; m_SelectedImage->GetGeometry()->IndexToWorld(index, max); this->m_WorldMaxList.push_back(max); index[0] = s[t]->GetMinIndex()[0]; index[1] = s[t]->GetMinIndex()[1]; index[2] = s[t]->GetMinIndex()[2]; m_SelectedImage->GetGeometry()->IndexToWorld(index, min); this->m_WorldMinList.push_back(min); } typedef mitk::ImageStatisticsCalculator::StatisticsContainer::RealType RealType; RealType maxVal = std::numeric_limits<RealType>::max(); this->m_Controls->m_StatisticsTable->setItem( 0, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetMean(), 0, 'f', decimals) ) ); this->m_Controls->m_StatisticsTable->setItem( 1, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetMedian(), 0, 'f', decimals) ) ); this->m_Controls->m_StatisticsTable->setItem( 2, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetStd(), 0, 'f', decimals) ) ); this->m_Controls->m_StatisticsTable->setItem( 3, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetRMS(), 0, 'f', decimals) ) ); QString max; max.append(QString("%1").arg(s[t]->GetMax(), 0, 'f', decimals)); max += " ("; for (int i=0; i<s[t]->GetMaxIndex().size(); i++) { max += QString::number(s[t]->GetMaxIndex()[i]); if (i<s[t]->GetMaxIndex().size()-1) max += ","; } max += ")"; this->m_Controls->m_StatisticsTable->setItem( 4, t, new QTableWidgetItem( max ) ); QString min; min.append(QString("%1").arg(s[t]->GetMin(), 0, 'f', decimals)); min += " ("; for (int i=0; i<s[t]->GetMinIndex().size(); i++) { min += QString::number(s[t]->GetMinIndex()[i]); if (i<s[t]->GetMinIndex().size()-1) min += ","; } min += ")"; this->m_Controls->m_StatisticsTable->setItem( 5, t, new QTableWidgetItem( min ) ); this->m_Controls->m_StatisticsTable->setItem( 6, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetN()) ) ); const mitk::BaseGeometry *geometry = image->GetGeometry(); if ( geometry != nullptr ) { const mitk::Vector3D &spacing = image->GetGeometry()->GetSpacing(); double volume = spacing[0] * spacing[1] * spacing[2] * (double) s[t]->GetN(); this->m_Controls->m_StatisticsTable->setItem( 7, t, new QTableWidgetItem( QString("%1").arg(volume, 0, 'f', decimals) ) ); } else { this->m_Controls->m_StatisticsTable->setItem( 7, t, new QTableWidgetItem( "NA" ) ); } //statistics of higher order should have 5 decimal places because they used to be very small this->m_Controls->m_StatisticsTable->setItem( 8, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetSkewness(), 0, 'f', 5) ) ); this->m_Controls->m_StatisticsTable->setItem( 9, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetKurtosis(), 0, 'f', 5) ) ); this->m_Controls->m_StatisticsTable->setItem( 10, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetUniformity(), 0, 'f', 5) ) ); this->m_Controls->m_StatisticsTable->setItem( 11, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetEntropy(), 0, 'f', 5) ) ); this->m_Controls->m_StatisticsTable->setItem( 12, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetMPP(), 0, 'f', decimals) ) ); this->m_Controls->m_StatisticsTable->setItem( 13, t, new QTableWidgetItem( QString("%1").arg(s[t]->GetUPP(), 0, 'f', 5) ) ); } this->m_Controls->m_StatisticsTable->resizeColumnsToContents(); int height = STAT_TABLE_BASE_HEIGHT; if (this->m_Controls->m_StatisticsTable->horizontalHeader()->isVisible()) height += this->m_Controls->m_StatisticsTable->horizontalHeader()->height(); if (this->m_Controls->m_StatisticsTable->horizontalScrollBar()->isVisible()) height += this->m_Controls->m_StatisticsTable->horizontalScrollBar()->height(); this->m_Controls->m_StatisticsTable->setMinimumHeight(height); // make sure the current timestep's column is highlighted (and the correct histogram is displayed) unsigned int t = this->GetRenderWindowPart()->GetTimeNavigationController()->GetTime()-> GetPos(); mitk::SliceNavigationController::GeometryTimeEvent timeEvent(this->m_SelectedImage->GetTimeGeometry(), t); this->OnTimeChanged(timeEvent); t = std::min(image->GetTimeSteps() - 1, t); // See bug 18340 /*QString hotspotMean; hotspotMean.append(QString("%1").arg(s[t].GetHotspotStatistics().GetMean(), 0, 'f', decimals)); hotspotMean += " ("; for (int i=0; i<s[t].GetHotspotIndex().size(); i++) { hotspotMean += QString::number(s[t].GetHotspotIndex()[i]); if (i<s[t].GetHotspotIndex().size()-1) hotspotMean += ","; } hotspotMean += ")"; this->m_Controls->m_StatisticsTable->setItem( 7, t, new QTableWidgetItem( hotspotMean ) ); QString hotspotMax; hotspotMax.append(QString("%1").arg(s[t].GetHotspotStatistics().GetMax(), 0, 'f', decimals)); hotspotMax += " ("; for (int i=0; i<s[t].GetHotspotStatistics().GetMaxIndex().size(); i++) { hotspotMax += QString::number(s[t].GetHotspotStatistics().GetMaxIndex()[i]); if (i<s[t].GetHotspotStatistics().GetMaxIndex().size()-1) hotspotMax += ","; } hotspotMax += ")"; this->m_Controls->m_StatisticsTable->setItem( 8, t, new QTableWidgetItem( hotspotMax ) ); QString hotspotMin; hotspotMin.append(QString("%1").arg(s[t].GetHotspotStatistics().GetMin(), 0, 'f', decimals)); hotspotMin += " ("; for (int i=0; i<s[t].GetHotspotStatistics().GetMinIndex().size(); i++) { hotspotMin += QString::number(s[t].GetHotspotStatistics().GetMinIndex()[i]); if (i<s[t].GetHotspotStatistics().GetMinIndex().size()-1) hotspotMin += ","; } hotspotMin += ")"; this->m_Controls->m_StatisticsTable->setItem( 9, t, new QTableWidgetItem( hotspotMin ) );*/ } std::vector<QString> QmitkImageStatisticsView::CalculateStatisticsForPlanarFigure( const mitk::Image *image) { std::vector<QString> result; int decimals = 2; mitk::PixelType doublePix = mitk::MakeScalarPixelType< double >(); mitk::PixelType floatPix = mitk::MakeScalarPixelType< float >(); if (image->GetPixelType()==doublePix || image->GetPixelType()==floatPix) { decimals = 5; } mitk::ImageStatisticsCalculator::StatisticsContainer::Pointer stats = m_Controls->m_JSHistogram->GetStatistics(); typedef mitk::ImageStatisticsCalculator::StatisticsContainer::RealType RealType; RealType maxVal = std::numeric_limits<RealType>::max(); if (stats->GetMean() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg(stats->GetMean(), 0, 'f', decimals)); } if (stats->GetMedian() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg(stats->GetMedian(), 0, 'f', decimals)); } if (stats->GetStd() == maxVal) { result.push_back(QString("NA")); } else { result.push_back( QString("%1").arg( stats->GetStd(), 0, 'f', decimals)); } if (stats->GetRMS() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg( stats->GetRMS(), 0, 'f', decimals)); } if (stats->GetMax() == maxVal) { result.push_back(QString("NA")); } else { QString max; max.append(QString("%1").arg(stats->GetMax(), 0, 'f', decimals)); result.push_back(max); } if (stats->GetMin() == maxVal) { result.push_back(QString("NA")); } else { QString min; min.append(QString("%1").arg(stats->GetMin(), 0, 'f', decimals)); result.push_back(min); } if (stats->GetN() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg(stats->GetN())); } result.push_back(QString("NA")); //statistics of higher order should have 5 decimal places because they used to be very small if (stats->GetSkewness() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg(stats->GetSkewness(), 0, 'f', 5 )); } if (stats->GetKurtosis() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg(stats->GetKurtosis(), 0, 'f', 5) ); } if (stats->GetUniformity() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg(stats->GetUniformity(), 0, 'f', 5) ); } if (stats->GetEntropy() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg(stats->GetEntropy(), 0, 'f', 5) ); } if (stats->GetMPP() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg(stats->GetMPP(), 0, 'f', decimals) ); } if (stats->GetUPP() == maxVal) { result.push_back(QString("NA")); } else { result.push_back(QString("%1").arg(stats->GetUPP(), 0, 'f', 5) ); } return result; } void QmitkImageStatisticsView::FillLinearProfileStatisticsTableView( const mitk::Image *image ) { this->m_Controls->m_StatisticsTable->setColumnCount(1); this->m_Controls->m_StatisticsTable->horizontalHeader()->setVisible(false); m_PlanarFigureStatistics = this->CalculateStatisticsForPlanarFigure(image); for (int i = 0; i< m_PlanarFigureStatistics.size(); i++) { this->m_Controls->m_StatisticsTable->setItem( i, 0, new QTableWidgetItem(m_PlanarFigureStatistics[i] )); } this->m_Controls->m_StatisticsTable->resizeColumnsToContents(); int height = STAT_TABLE_BASE_HEIGHT; if (this->m_Controls->m_StatisticsTable->horizontalHeader()->isVisible()) height += this->m_Controls->m_StatisticsTable->horizontalHeader()->height(); if (this->m_Controls->m_StatisticsTable->horizontalScrollBar()->isVisible()) height += this->m_Controls->m_StatisticsTable->horizontalScrollBar()->height(); this->m_Controls->m_StatisticsTable->setMinimumHeight(height); } void QmitkImageStatisticsView::InvalidateStatisticsTableView() { this->m_Controls->m_StatisticsTable->horizontalHeader()->setVisible(false); this->m_Controls->m_StatisticsTable->setColumnCount(1); for ( unsigned int i = 0; i < this->m_Controls->m_StatisticsTable->rowCount(); ++i ) { { this->m_Controls->m_StatisticsTable->setItem( i, 0, new QTableWidgetItem( "NA" ) ); } } this->m_Controls->m_StatisticsTable->setMinimumHeight(STAT_TABLE_BASE_HEIGHT); } void QmitkImageStatisticsView::Activated() { } void QmitkImageStatisticsView::Deactivated() { } void QmitkImageStatisticsView::Visible() { m_Visible = true; mitk::IRenderWindowPart* renderWindow = GetRenderWindowPart(); if (renderWindow) { itk::ReceptorMemberCommand<QmitkImageStatisticsView>::Pointer cmdTimeEvent = itk::ReceptorMemberCommand<QmitkImageStatisticsView>::New(); cmdTimeEvent->SetCallbackFunction(this, &QmitkImageStatisticsView::OnTimeChanged); // It is sufficient to add the observer to the axial render window since the GeometryTimeEvent // is always triggered by all views. m_TimeObserverTag = renderWindow->GetQmitkRenderWindow("axial")-> GetSliceNavigationController()-> AddObserver(mitk::SliceNavigationController::GeometryTimeEvent(nullptr, 0), cmdTimeEvent); } if (m_DataNodeSelectionChanged) { if (this->IsCurrentSelectionValid()) { this->SelectionChanged(this->GetCurrentSelection()); } else { this->SelectionChanged(this->GetDataManagerSelection()); } m_DataNodeSelectionChanged = false; } } void QmitkImageStatisticsView::Hidden() { m_Visible = false; // The slice navigation controller observer is removed here instead of in the destructor. // If it was called in the destructor, the application would freeze because the view's // destructor gets called after the render windows have been destructed. - if ( m_TimeObserverTag != nullptr ) + if ( m_TimeObserverTag != 0 ) { mitk::IRenderWindowPart* renderWindow = GetRenderWindowPart(); if (renderWindow) { renderWindow->GetQmitkRenderWindow("axial")->GetSliceNavigationController()-> RemoveObserver( m_TimeObserverTag ); } - m_TimeObserverTag = nullptr; + m_TimeObserverTag = 0; } } void QmitkImageStatisticsView::SetFocus() { }