void QmitkImageStatisticsView::OnSelectionChanged( std::vector<mitk::DataNode*> nodes ) { // Clear any unreferenced images this->RemoveOrphanImages(); if ( !this->IsVisible() ) { return; } // Check if selection makeup consists only of valid nodes: // One image, segmentation or planarFigure // One image and one of the other two bool tooManyNodes( true ); bool invalidNodes( true ); if ( nodes.size() < 3 ) { tooManyNodes = false; } if( !tooManyNodes ) { unsigned int numberImages = 0; unsigned int numberSegmentations = 0; unsigned int numberPlanarFigures = 0; for ( unsigned int index = 0; index < nodes.size(); index++ ) { m_SelectedImageMask = dynamic_cast< mitk::Image * >( nodes[ index ]->GetData() ); m_SelectedPlanarFigure = dynamic_cast< mitk::PlanarFigure * >( nodes[ index ]->GetData() ); if ( m_SelectedImageMask != NULL ) { bool isMask( false ); nodes[ index ]->GetPropertyValue("binary", isMask); if ( !isMask ) { numberImages++; } else { numberSegmentations++; if ( numberImages != 0 ) // image should be last element { std::swap( nodes[ index ], nodes[ index - 1 ] ); } } } else if ( m_SelectedPlanarFigure != NULL ) { numberPlanarFigures++; if ( numberImages != 0 ) // image should be last element { std::swap( nodes[ index ], nodes[ index - 1 ] ); } } } if ( ( numberPlanarFigures + numberSegmentations + numberImages ) == nodes.size() && //No invalid nodes ( numberPlanarFigures + numberSegmentations ) < 2 && numberImages < 2 // maximum of one image and/or one of either planar figure or segmentation ) { invalidNodes = false; } } if ( nodes.empty() || tooManyNodes || invalidNodes ) { // Nothing to do: invalidate image, clear statistics, histogram, and GUI m_SelectedImage = NULL; this->InvalidateStatisticsTableView() ; m_Controls->m_HistogramWidget->ClearItemModel(); m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_SelectedMaskLabel->setText( "None" ); return; } // Get selected element mitk::DataNode *selectedNode = nodes.front(); mitk::Image *selectedImage = dynamic_cast< mitk::Image * >( selectedNode->GetData() ); // Find the next parent/grand-parent node containing an image, if any mitk::DataStorage::SetOfObjects::ConstPointer parentObjects; mitk::DataNode *parentNode = NULL; mitk::Image *parentImage = NULL; // Possibly previous change listeners if ( (m_SelectedPlanarFigure != NULL) && (m_PlanarFigureObserverTag >= 0) ) { m_SelectedPlanarFigure->RemoveObserver( m_PlanarFigureObserverTag ); m_PlanarFigureObserverTag = -1; } if ( (m_SelectedImage != NULL) && (m_ImageObserverTag >= 0) ) { m_SelectedImage->RemoveObserver( m_ImageObserverTag ); m_ImageObserverTag = -1; } if ( (m_SelectedImageMask != NULL) && (m_ImageMaskObserverTag >= 0) ) { m_SelectedImageMask->RemoveObserver( m_ImageMaskObserverTag ); m_ImageMaskObserverTag = -1; } // Deselect all images and masks by default m_SelectedImageNode = NULL; m_SelectedImage = NULL; m_SelectedMaskNode = NULL; m_SelectedImageMask = NULL; m_SelectedPlanarFigure = NULL; { unsigned int parentObjectIndex = 0; parentObjects = this->GetDefaultDataStorage()->GetSources( selectedNode ); while( parentObjectIndex < parentObjects->Size() ) { // Use first parent object (if multiple parents are present) parentNode = parentObjects->ElementAt( parentObjectIndex ); parentImage = dynamic_cast< mitk::Image * >( parentNode->GetData() ); if( parentImage != NULL ) { break; } parentObjectIndex++; } } if ( nodes.size() == 2 ) { parentNode = nodes.back(); parentImage = dynamic_cast< mitk::Image * >( parentNode->GetData() ); } if ( parentImage != NULL ) { m_SelectedImageNode = parentNode; m_SelectedImage = parentImage; // Check if a valid mask has been selected (Image or PlanarFigure) m_SelectedImageMask = dynamic_cast< mitk::Image * >( selectedNode->GetData() ); m_SelectedPlanarFigure = dynamic_cast< mitk::PlanarFigure * >( selectedNode->GetData() ); // Check whether ImageMask is a binary segmentation if ( (m_SelectedImageMask != NULL) ) { bool isMask( false ); selectedNode->GetPropertyValue("binary", isMask); if ( !isMask ) { m_SelectedImageNode = selectedNode; m_SelectedImage = selectedImage; m_SelectedImageMask = NULL; } else { m_SelectedMaskNode = selectedNode; } } else if ( (m_SelectedPlanarFigure != NULL) ) { m_SelectedMaskNode = selectedNode; } } else if ( selectedImage != NULL ) { m_SelectedImageNode = selectedNode; m_SelectedImage = selectedImage; } typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; ITKCommandType::Pointer changeListener; changeListener = ITKCommandType::New(); changeListener->SetCallbackFunction( this, &QmitkImageStatisticsView::RequestStatisticsUpdate ); // Add change listeners to selected objects if ( m_SelectedImage != NULL ) { m_ImageObserverTag = m_SelectedImage->AddObserver( itk::ModifiedEvent(), changeListener ); } if ( m_SelectedImageMask != NULL ) { m_ImageMaskObserverTag = m_SelectedImageMask->AddObserver( itk::ModifiedEvent(), changeListener ); } if ( m_SelectedPlanarFigure != NULL ) { m_PlanarFigureObserverTag = m_SelectedPlanarFigure->AddObserver( mitk::EndInteractionPlanarFigureEvent(), changeListener ); } // Clear statistics / histogram GUI if nothing is selected if ( m_SelectedImage == NULL ) { // Clear statistics, histogram, and GUI this->InvalidateStatisticsTableView(); m_Controls->m_HistogramWidget->ClearItemModel(); m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_SelectedMaskLabel->setText( "None" ); } else { // Else, request statistics and GUI update this->RequestStatisticsUpdate(); } }
void QmitkImageStatisticsView::UpdateStatistics() { // Remove any cached images that are no longer referenced elsewhere this->RemoveOrphanImages(); QmitkStdMultiWidget *multiWidget = this->GetActiveStdMultiWidget(); if ( multiWidget == NULL ) { return; } unsigned int timeStep = multiWidget->GetTimeNavigationController()->GetTime()->GetPos(); if ( m_SelectedImage != NULL ) { // 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_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; return; } // Retrieve ImageStatisticsCalculator from has map (or create a new one // for this image if non-existant) ImageStatisticsMapType::iterator it = m_ImageStatisticsMap.find( m_SelectedImage ); if ( it != m_ImageStatisticsMap.end() ) { m_CurrentStatisticsCalculator = it->second; MITK_INFO << "Retrieving StatisticsCalculator"; } else { m_CurrentStatisticsCalculator = mitk::ImageStatisticsCalculator::New(); m_CurrentStatisticsCalculator->SetImage( m_SelectedImage ); m_ImageStatisticsMap[m_SelectedImage] = m_CurrentStatisticsCalculator; MITK_INFO << "Creating StatisticsCalculator"; } std::string maskName; std::string maskType; unsigned int maskDimension; if ( m_SelectedImageMask != NULL ) { m_CurrentStatisticsCalculator->SetImageMask( m_SelectedImageMask ); m_CurrentStatisticsCalculator->SetMaskingModeToImage(); maskName = m_SelectedMaskNode->GetName(); maskType = m_SelectedImageMask->GetNameOfClass(); maskDimension = 3; } else if ( m_SelectedPlanarFigure != NULL ) { m_CurrentStatisticsCalculator->SetPlanarFigure( m_SelectedPlanarFigure ); m_CurrentStatisticsCalculator->SetMaskingModeToPlanarFigure(); maskName = m_SelectedMaskNode->GetName(); maskType = m_SelectedPlanarFigure->GetNameOfClass(); maskDimension = 2; } else { m_CurrentStatisticsCalculator->SetMaskingModeToNone(); maskName = "None"; maskType = ""; maskDimension = 0; } if(m_Controls->m_IgnoreZerosCheckbox->isChecked()) { m_CurrentStatisticsCalculator->SetIgnorePixelValue(0); m_CurrentStatisticsCalculator->SetDoIgnorePixelValue(true); } else { m_CurrentStatisticsCalculator->SetDoIgnorePixelValue(false); } std::stringstream maskLabel; maskLabel << maskName; if ( maskDimension > 0 ) { maskLabel << " [" << maskDimension << "D " << maskType << "]"; } m_Controls->m_SelectedMaskLabel->setText( maskLabel.str().c_str() ); bool statisticsChanged = false; bool statisticsCalculationSuccessful = false; // Initialize progress bar mitk::ProgressBar::GetInstance()->AddStepsToDo( 100 ); // Install listener for progress events and initialize progress bar typedef itk::SimpleMemberCommand< QmitkImageStatisticsView > ITKCommandType; ITKCommandType::Pointer progressListener; progressListener = ITKCommandType::New(); progressListener->SetCallbackFunction( this, &QmitkImageStatisticsView::UpdateProgressBar ); unsigned long progressObserverTag = m_CurrentStatisticsCalculator ->AddObserver( itk::ProgressEvent(), progressListener ); // show wait cursor this->WaitCursorOn(); try { // Compute statistics statisticsChanged = m_CurrentStatisticsCalculator->ComputeStatistics( timeStep ); statisticsCalculationSuccessful = true; } 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(); } 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(); } m_CurrentStatisticsCalculator->RemoveObserver( progressObserverTag ); // Make sure that progress bar closes mitk::ProgressBar::GetInstance()->Progress( 100 ); // remove wait cursor this->WaitCursorOff(); if ( statisticsCalculationSuccessful ) { if ( statisticsChanged ) { // Do not show any error messages m_Controls->m_ErrorMessageLabel->hide(); m_CurrentStatisticsValid = true; } m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 0 ); m_Controls->m_HistogramWidget->SetHistogramModeToDirectHistogram(); m_Controls->m_HistogramWidget->SetHistogram( m_CurrentStatisticsCalculator->GetHistogram( timeStep ) ); m_Controls->m_HistogramWidget->UpdateItemModelFromHistogram(); MITK_INFO << "UpdateItemModelFromHistogram()"; this->FillStatisticsTableView( m_CurrentStatisticsCalculator->GetStatistics( timeStep ), m_SelectedImage ); } else { m_Controls->m_SelectedMaskLabel->setText( "None" ); // Clear statistics and histogram this->InvalidateStatisticsTableView(); m_Controls->m_HistogramWidget->ClearItemModel(); m_CurrentStatisticsValid = false; // If a (non-closed) PlanarFigure is selected, display a line profile widget if ( m_SelectedPlanarFigure != NULL ) { // check whether PlanarFigure is initialized const mitk::Geometry2D *planarFigureGeometry2D = m_SelectedPlanarFigure->GetGeometry2D(); if ( planarFigureGeometry2D == NULL ) { // Clear statistics, histogram, and GUI this->InvalidateStatisticsTableView(); m_Controls->m_HistogramWidget->ClearItemModel(); m_Controls->m_LineProfileWidget->ClearItemModel(); m_CurrentStatisticsValid = false; m_Controls->m_ErrorMessageLabel->hide(); m_Controls->m_SelectedMaskLabel->setText( "None" ); return; } // TODO: enable line profile widget m_Controls->m_StatisticsWidgetStack->setCurrentIndex( 1 ); m_Controls->m_LineProfileWidget->SetImage( m_SelectedImage ); m_Controls->m_LineProfileWidget->SetPlanarFigure( m_SelectedPlanarFigure ); m_Controls->m_LineProfileWidget->UpdateItemModelFromPath(); } } } }
void QmitkImageStatisticsView::UpdateStatistics() { mitk::IRenderWindowPart* renderPart = this->GetRenderWindowPart(); if ( renderPart == NULL ) { 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 == NULL && 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 == NULL) { 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 == 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 << "<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 != NULL && m_SelectedImage == NULL) { 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 == NULL) { 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 != 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 << "<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 != NULL) { 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 ); this->m_CalculationThread->SetHistogramBinSize(m_Controls->m_HistogramBinSizeSpinbox->value()); 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; } }