Example #1
0
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();
  }
}
Example #2
0
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;
  }
}