diff --git a/Modules/QmitkExt/resources/Histogram.js b/Modules/QmitkExt/resources/Histogram.js index ee8498466b..87fa8f5ee2 100644 --- a/Modules/QmitkExt/resources/Histogram.js +++ b/Modules/QmitkExt/resources/Histogram.js @@ -1,291 +1,289 @@ /** * @author Moritz Petry */ //var dataset = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 7, 18, 19, 22]; /*var indexNumber = Math.round(Math.random()*10+30); for (var i = 0; i< indexNumber; i++) { var newNumber = Math.random() * 100; dataset.push(newNumber); }*/ var dataset = new Array(); var frequency = new Array(); var measurement = new Array(); var dataset; var margin = { - top : 0, - bottom : 50, - left : 45, - right : 20, - }; + top : 0, + bottom : 50, + left : 45, + right : 20, + }; var height = histogramData.height - margin.top - margin.bottom; var width = histogramData.width - margin.left - margin.right; var tension = 0.8; var connected = false; var dur = 1000; var useLinePlot = false; // connecting signal from qt side with JavaScript method if (!connected) { - connected = true; - histogramData.DataChanged.connect(updateHistogram); + connected = true; + histogramData.DataChanged.connect(updateHistogram); } var xScale = d3.scale.linear() - .domain([d3.min(histogramData.measurement),d3.max(histogramData.measurement)]) - .range([0,width]); + .domain([d3.min(histogramData.measurement),d3.max(histogramData.measurement)]) + .range([0,width]); var yScale = d3.scale.linear() - .domain([0,d3.max(histogramData.frequency)]) - .range([height,margin.top]); + .domain([0,d3.max(histogramData.frequency)]) + .range([height,margin.top]); var xAxis = d3.svg.axis() - .scale(xScale) - .orient("bottom"); + .scale(xScale) + .orient("bottom"); var yAxis = d3.svg.axis() - .scale(yScale) - .orient("left"); + .scale(yScale) + .orient("left"); var svg = d3.select("body") - .append("svg") - .attr("class", "svg") - .attr("width", width + margin.right + margin.left) - .attr("height", height + margin.top + margin.bottom) - .append("g") - .attr("transform", "translate (" + margin.left + "," + margin.top + ")") - .call(d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([1, 10]).on("zoom", zoom)); + .append("svg") + .attr("class", "svg") + .attr("width", width + margin.right + margin.left) + .attr("height", height + margin.top + margin.bottom) + .append("g") + .attr("transform", "translate (" + margin.left + "," + margin.top + ")") + .call(d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([1, 10]).on("zoom", zoom)); var vis = svg.append("svg") - .attr("width", width) - .attr("height", height); + .attr("width", width) + .attr("height", height); svg.append("rect") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .attr("opacity", 0); + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .attr("opacity", 0); updateHistogram(); // method to update and choose histogram function updateHistogram() { - if (!histogramData.useLineGraph) - { - barChart(); - } - else if (histogramData.useLineGraph) - { - linePlot() - } + if (!histogramData.useLineGraph) + { + barChart(); + } + else if (histogramData.useLineGraph) + { + linePlot() + } } // method to display histogram as barchart function barChart() { // match scale to current data - xScale = d3.scale.linear() - .domain([d3.min(histogramData.measurement),d3.max(histogramData.measurement)]) - .range([0,width]); - - yScale = d3.scale.linear() - .domain([0,d3.max(histogramData.frequency)]) - .range([height,margin.top]); - - xAxis = d3.svg.axis() - .scale(xScale) - .ticks(5) - .orient("bottom"); - - yAxis = d3.svg.axis() - .scale(yScale) - .orient("left"); - - svg.call(d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([1, 10]).on("zoom", zoom)); - - var linenull = d3.svg.line() - .interpolate("linear") - .x(function(d,i) { - return xScale(histogramData.measurement[i]); - }) - .y(function(d) { - return yScale(0); - }); + xScale = d3.scale.linear() + .domain([d3.min(histogramData.measurement),d3.max(histogramData.measurement)]) + .range([0,width]); + + yScale = d3.scale.linear() + .domain([0,d3.max(histogramData.frequency)]) + .range([height,margin.top]); + + xAxis = d3.svg.axis() + .scale(xScale) + .ticks(5) + .orient("bottom"); + + yAxis = d3.svg.axis() + .scale(yScale) + .orient("left"); + + svg.call(d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([1, 10]).on("zoom", zoom)); + + var linenull = d3.svg.line() + .interpolate("linear") + .x(function(d,i) { + return xScale(histogramData.measurement[i]); + }) + .y(function(d) { + return yScale(0); + }); // element to animate transition from linegraph to barchart - vis.selectAll("path.line").transition().duration(dur).attr("d", linenull(histogramData.frequency)).remove(); - - var bar = vis.selectAll("rect.bar").data(histogramData.frequency); - - bar.enter().append("rect") - .attr("class", "bar") - .on("mouseover", function (d) {d3.select(this).style('fill', "red");}) - .on("mouseout", function (d) {d3.select(this).style("fill", "steelblue");}) - .attr("x", function(d,i) { - return xScale(histogramData.measurement[i]); - }) - .attr("y", height) - .attr("height", 0) - .attr("width", (width/(histogramData.frequency.length + 1))) - .transition().delay(dur).duration(dur*1.5) - .attr("height", function(d) { - return (height - yScale(d)); - }) - .attr("width", (width/(histogramData.frequency.length + 1))) - .attr("y", function(d) { - return yScale(d); - }); - - bar.transition().delay(dur).duration(dur*1.5) - .attr("x", function(d,i) { - return xScale(histogramData.measurement[i]); - }) - .attr("y", function(d) { - return yScale(d); - }) - .attr("height", function(d) { - return (height - yScale(d)); - }) - .attr("width", (width/(histogramData.frequency.length + 1))) - - bar.exit().transition().delay(dur).duration(dur*1.5) - .attr("y", height) - .attr("height", 0) - .remove(); - - svg.selectAll("g") - .transition() - .duration(dur) - .attr("opacity", 0) - .remove(); - - svg.append("g") - .attr("class", "x axis") - .attr("transform", "translate(0," + height + ")") - .transition().duration(dur) - .attr("opacity", 100) - .call(xAxis); - - svg.append("g") - .attr("class", "y axis") - .transition().duration(dur) - .attr("opacity", 100) - .call(yAxis); + vis.selectAll("path.line").transition().duration(dur).attr("d", linenull(histogramData.frequency)).remove(); + + var bar = vis.selectAll("rect.bar").data(histogramData.frequency); + + bar.enter().append("rect") + .attr("class", "bar") + .on("mouseover", function (d) {d3.select(this).style('fill', "red");}) + .on("mouseout", function (d) {d3.select(this).style("fill", "steelblue");}) + .attr("x", function(d,i) { + return xScale(histogramData.measurement[i]); + }) + .attr("y", height) + .attr("height", 0) + .attr("width", barWidth) + .transition().delay(dur).duration(dur*1.5) + .attr("height", function(d) { + return (height - yScale(d)); + }) + .attr("width", barWidth) + .attr("y", function(d) { + return yScale(d); + }); + + bar.transition().delay(dur).duration(dur*1.5) + .attr("x", function(d,i) { + return xScale(histogramData.measurement[i]); + }) + .attr("y", function(d) { + return yScale(d); + }) + .attr("height", function(d) { + return (height - yScale(d)); + }) + .attr("width", barWidth) + + bar.exit().transition().delay(dur).duration(dur*1.5) + .attr("y", height) + .attr("height", 0) + .remove(); + + svg.selectAll("g") + .transition() + .duration(dur) + .attr("opacity", 0) + .remove(); + + svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + height + ")") + .transition().duration(dur) + .attr("opacity", 100) + .call(xAxis); + + svg.append("g") + .attr("class", "y axis") + .transition().duration(dur) + .attr("opacity", 100) + .call(yAxis); } // method to display histogram as linegraph function linePlot() { // match scale to current data - xScale = d3.scale.linear() - .domain([d3.min(histogramData.measurement),d3.max(histogramData.measurement)]) - .range([0,width]); + xScale = d3.scale.linear() + .domain([d3.min(histogramData.measurement),d3.max(histogramData.measurement)]) + .range([0,width]); - yScale = d3.scale.linear() - .domain([0,d3.max(histogramData.frequency)]) - .range([height,margin.top]); + yScale = d3.scale.linear() + .domain([0,d3.max(histogramData.frequency)]) + .range([height,margin.top]); - xAxis = d3.svg.axis() - .scale(xScale) - .ticks(5) - .orient("bottom"); + xAxis = d3.svg.axis() + .scale(xScale) + .ticks(5) + .orient("bottom"); - yAxis = d3.svg.axis() - .scale(yScale) - .orient("left"); + yAxis = d3.svg.axis() + .scale(yScale) + .orient("left"); - svg.call(d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([1, 10]).on("zoom", zoom)); + svg.call(d3.behavior.zoom().x(xScale).y(yScale).scaleExtent([1, 10]).on("zoom", zoom)); // element to animate transition from barchart to linegraph - vis.selectAll("rect.bar") - .transition() - .duration(dur) - .attr("height", 0) - .attr("fill", "black") - .remove(); - - var line = d3.svg.line() - .interpolate("cardinal") - .x(function(d,i) { - return xScale(histogramData.measurement[i]); - }) - .y(function(d) { - return yScale(d); - }) - .tension(0.8); - - var linenull = d3.svg.line() - .interpolate("cardinal") - .x(function(d,i) { - return xScale(histogramData.measurement[i]); - }) - .y(function(d) { - return yScale(0); - }) - .tension(0.8); - - var graph = vis.selectAll("path.line").data([histogramData.frequency]); - - - graph.enter() - .append("path") - .attr("class", "line") - .attr("d", linenull) - .transition() - .duration(dur) - .attr("d", line); - - - graph.transition() - .duration(dur) - .attr("d", line); - - graph.exit().transition().duration(dur).attr("d", linenull); - - svg.selectAll("g") - .transition() - .duration(dur) - .attr("opacity", 0) - .remove(); - - svg.append("g") - .attr("class", "x axis") - .attr("transform", "translate(0," + height + ")") - .transition().duration(dur) - .attr("opacity", 100) - .call(xAxis); - - svg.append("g") - .attr("class", "y axis") - .transition().duration(dur) - .attr("opacity", 100) - .call(yAxis); + vis.selectAll("rect.bar") + .transition() + .duration(dur) + .attr("height", 0) + .attr("fill", "black") + .remove(); + + var line = d3.svg.line() + .interpolate("cardinal") + .x(function(d,i) { + return xScale(histogramData.measurement[i]); + }) + .y(function(d) { + return yScale(d); + }) + .tension(0.8); + + var linenull = d3.svg.line() + .interpolate("cardinal") + .x(function(d,i) { + return xScale(histogramData.measurement[i]); + }) + .y(function(d) { + return yScale(0); + }) + .tension(0.8); + + var graph = vis.selectAll("path.line").data([histogramData.frequency]); + + graph.enter() + .append("path") + .attr("class", "line") + .attr("d", linenull) + .transition() + .duration(dur) + .attr("d", line); + + graph.transition() + .duration(dur) + .attr("d", line); + + graph.exit().transition().duration(dur).attr("d", linenull); + + svg.selectAll("g") + .transition() + .duration(dur) + .attr("opacity", 0) + .remove(); + + svg.append("g") + .attr("class", "x axis") + .attr("transform", "translate(0," + height + ")") + .transition().duration(dur) + .attr("opacity", 100) + .call(xAxis); + + svg.append("g") + .attr("class", "y axis") + .transition().duration(dur) + .attr("opacity", 100) + .call(yAxis); } // method to ensure barwidth is not smaller than 1px function barWidth() { - var barWidth = width/data.length - 1; - if (barWidth < 1) - { - barWidth = 1; - } - return barWidth; + var bw = width/histogramData.frequency.length - 1; + if (bw < 1) + { + bw = 1; + } + return bw; } function zoom() { svg.select(".x.axis").call(xAxis); svg.select(".y.axis").call(yAxis); vis.selectAll(".bar").attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); vis.selectAll("path.line").attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")"); } svg.append("rect") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) - .attr("opacity", 0); + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) + .attr("opacity", 0); 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 ed8461e566..f62ebc9d67 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -1,701 +1,700 @@ /*=================================================================== 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 // berry includes #include // mitk includes #include "mitkNodePredicateDataType.h" #include "mitkPlanarFigureInteractor.h" // itk includes #include "itksys/SystemTools.hxx" #include #include const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics"; QmitkImageStatisticsView::QmitkImageStatisticsView(QObject* /*parent*/, const char* /*name*/) : m_Controls( NULL ), 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; } 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*)(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->m_Controls->m_JSHistogram), SLOT(histogramToBarChart())); connect( (QObject*) (this->m_Controls->m_lineRadioButton), SIGNAL(clicked()), (QObject*) (this->m_Controls->m_JSHistogram), SLOT(histogramToLineGraph())); - connect( (QObject*) (this->m_Controls->m_resetButton), SIGNAL(clicked()), (QObject*) (this->m_Controls->m_JSHistogram), SLOT(resetView())); } } void QmitkImageStatisticsView::JumpToCoordinates(int row ,int col) { mitk::Point3D world; if (row==4) world = m_WorldMin; else if (row==3) world = m_WorldMax; else return; mitk::IRenderWindowPart* part = this->GetRenderWindowPart(); if (part) { part->GetRenderWindow("axial")->GetSliceNavigationController()->SelectSliceByPoint(world); part->GetRenderWindow("sagittal")->GetSliceNavigationController()->SelectSliceByPoint(world); part->GetRenderWindow("coronal")->GetSliceNavigationController()->SelectSliceByPoint(world); } } void QmitkImageStatisticsView::OnIgnoreZerosCheckboxClicked() { emit StatisticsUpdate(); } void QmitkImageStatisticsView::OnClipboardHistogramButtonClicked() { if ( m_CurrentStatisticsValid ) { typedef mitk::ImageStatisticsCalculator::HistogramType HistogramType; const HistogramType *histogram = this->m_CalculationThread->GetTimeStepHistogram().GetPointer(); QString clipboard( "Measurement \t Frequency\n" ); for ( HistogramType::ConstIterator it = histogram->Begin(); 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::OnClipboardStatisticsButtonClicked() { if ( this->m_CurrentStatisticsValid ) { const mitk::ImageStatisticsCalculator::Statistics &statistics = 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::OnSelectionChanged( berry::IWorkbenchPart::Pointer /*part*/, const QList &selectedNodes ) { if (this->m_Visible) { this->SelectionChanged( selectedNodes ); } else { this->m_DataNodeSelectionChanged = true; } } void QmitkImageStatisticsView::SelectionChanged(const QList &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; } this->ReinitData(); if(selectedNodes.size() == 1 || selectedNodes.size() == 2) { 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 != NULL) { this->m_SelectedImage->RemoveObserver( this->m_ImageObserverTag); this->m_SelectedImage = NULL; } if(this->m_SelectedImageMask != NULL) { this->m_SelectedImageMask->RemoveObserver( this->m_ImageMaskObserverTag); this->m_SelectedImageMask = NULL; } if(this->m_SelectedPlanarFigure != NULL) { 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(); m_Controls->m_JSHistogram->clearHistogram(); } 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 == NULL ) { this->m_StatisticsUpdatePending = false; return; } m_WorldMin.Fill(-1); m_WorldMax.Fill(-1); // 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::DataNode::Pointer planarFigureNode; 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( this->m_SelectedImageMask == NULL && isMask) { 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( !isMask ) { if(this->m_SelectedImage == NULL) { this->m_SelectedImage = static_cast(this->m_SelectedDataNodes.at(i)->GetData()); this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener); } } } else if (planarFig.IsNotNull()) { if(this->m_SelectedPlanarFigure == NULL) { 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 << "" << "Invalid data node type!" << ""; m_Controls->m_ErrorMessageLabel->setText( message.str().c_str() ); m_Controls->m_ErrorMessageLabel->show(); } } if(maskName == "") { maskName = "None"; maskType = ""; maskDimension = 0; } if (m_SelectedPlanarFigure != NULL && m_SelectedImage == NULL) { mitk::DataStorage::SetOfObjects::ConstPointer parentSet = this->GetDataStorage()->GetSources(planarFigureNode); for (int i=0; iSize(); i++) { mitk::DataNode::Pointer node = parentSet->ElementAt(i); if( imagePredicate->CheckNode(node) ) { bool isMask = false; node->GetPropertyValue("binary", isMask); if( !isMask ) { if(this->m_SelectedImage == NULL) { this->m_SelectedImage = static_cast(node->GetData()); this->m_ImageObserverTag = this->m_SelectedImage->AddObserver(itk::ModifiedEvent(), changeListener); } } } } } unsigned int timeStep = renderPart->GetTimeNavigationController()->GetTime()->GetPos(); 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; } 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; } //// 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 this->m_CalculationThread->start(); } catch ( const mitk::Exception& e) { std::stringstream message; message << "" << e.GetDescription() << ""; 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 << "" << 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(); 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 = NULL; } } 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::WriteStatisticsToGUI() { 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; } m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); m_Controls->m_HistogramWidget->SetHistogram( this->m_CalculationThread->GetTimeStepHistogram().GetPointer() ); m_Controls->m_JSHistogram->ComputeHistogram( 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" ); 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_HistogramWidget->ClearItemModel(); m_Controls->m_LineProfileWidget->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_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 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++) { 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_JSHistogram->ComputeHistogram( histogram ); m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); } void QmitkImageStatisticsView::FillStatisticsTableView( const mitk::ImageStatisticsCalculator::Statistics &s, const mitk::Image *image ) { if (s.MaxIndex.size()==3) { mitk::Point3D index; index[0] = s.MaxIndex[0]; index[1] = s.MaxIndex[1]; index[2] = s.MaxIndex[2]; m_SelectedImage->GetGeometry()->IndexToWorld(index, m_WorldMax); index[0] = s.MinIndex[0]; index[1] = s.MinIndex[1]; index[2] = s.MinIndex[2]; m_SelectedImage->GetGeometry()->IndexToWorld(index, m_WorldMin); } int decimals = 2; mitk::PixelType doublePix = mitk::MakeScalarPixelType< double >(); mitk::PixelType floatPix = mitk::MakeScalarPixelType< float >(); if (image->GetPixelType()==doublePix || image->GetPixelType()==floatPix) decimals = 5; this->m_Controls->m_StatisticsTable->setItem( 0, 0, new QTableWidgetItem( QString("%1").arg(s.Mean, 0, 'f', decimals) ) ); this->m_Controls->m_StatisticsTable->setItem( 0, 1, new QTableWidgetItem( QString("%1").arg(s.Sigma, 0, 'f', decimals) ) ); this->m_Controls->m_StatisticsTable->setItem( 0, 2, new QTableWidgetItem( QString("%1").arg(s.RMS, 0, 'f', decimals) ) ); QString max; max.append(QString("%1").arg(s.Max, 0, 'f', decimals)); max += " ("; for (int i=0; im_Controls->m_StatisticsTable->setItem( 0, 3, new QTableWidgetItem( max ) ); QString min; min.append(QString("%1").arg(s.Min, 0, 'f', decimals)); min += " ("; for (int i=0; im_Controls->m_StatisticsTable->setItem( 0, 4, new QTableWidgetItem( min ) ); 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', decimals) ) ); } 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; if (m_DataNodeSelectionChanged) { if (this->IsCurrentSelectionValid()) { this->SelectionChanged(this->GetCurrentSelection()); } else { this->SelectionChanged(this->GetDataManagerSelection()); } m_DataNodeSelectionChanged = false; } } void QmitkImageStatisticsView::Hidden() { m_Visible = false; } void QmitkImageStatisticsView::SetFocus() { } 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 cd0d5b7fa9..3cb45ddd5f 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsViewControls.ui @@ -1,436 +1,423 @@ QmitkImageStatisticsViewControls true 0 0 465 800 Form 0 0 Mask: None 2 Qt::Horizontal 40 20 color: rgb(255, 0, 0); Error Message Qt::AutoText Ignore zero-valued voxels Statistics 9 9 9 0 0 100 175 16777215 170 Qt::ScrollBarAlwaysOff Qt::ScrollBarAsNeeded true true true Qt::DotLine true false false 80 true 80 false true true false 25 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 0 0 0 35 Plot 150 10 85 17 0 0 0 0 Use linegraph 10 10 82 17 Use barchart true - - - - 280 - 10 - 75 - 23 - - - - Reset view - - 0 0 0 0 0 0 0 Copy to Clipboard Qt::Horizontal 40 20 Qt::Vertical 20 40 QmitkVtkHistogramWidget QWidget
QmitkVtkHistogramWidget.h
1
QmitkVtkLineProfileWidget QWidget
QmitkVtkLineProfileWidget.h
1
QmitkHistogramJSWidget QWidget
QmitkHistogramJSWidget.h
1