diff --git a/Modules/QmitkExt/QmitkSlicesInterpolator.cpp b/Modules/QmitkExt/QmitkSlicesInterpolator.cpp index 55b3db52db..d07c783a88 100644 --- a/Modules/QmitkExt/QmitkSlicesInterpolator.cpp +++ b/Modules/QmitkExt/QmitkSlicesInterpolator.cpp @@ -1,1008 +1,1007 @@ /*=================================================================== 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 "QmitkSlicesInterpolator.h" #include "QmitkStdMultiWidget.h" #include "QmitkSelectableGLWidget.h" #include "mitkToolManager.h" #include "mitkDataNodeFactory.h" #include "mitkLevelWindowProperty.h" #include "mitkColorProperty.h" #include "mitkProperties.h" #include "mitkRenderingManager.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkProgressBar.h" #include "mitkGlobalInteraction.h" #include "mitkOperationEvent.h" #include "mitkUndoController.h" #include "mitkInteractionConst.h" #include "mitkApplyDiffImageOperation.h" #include "mitkDiffImageApplier.h" #include "mitkSegTool2D.h" #include "mitkCoreObjectFactory.h" #include "mitkSurfaceToImageFilter.h" #include #include #include #include #include #include #include #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) const std::map QmitkSlicesInterpolator::createActionToSliceDimension() { std::map actionToSliceDimension; actionToSliceDimension[new QAction("Transversal (red window)", 0)] = 2; actionToSliceDimension[new QAction("Sagittal (green window)", 0)] = 0; actionToSliceDimension[new QAction("Coronal (blue window)", 0)] = 1; return actionToSliceDimension; } QmitkSlicesInterpolator::QmitkSlicesInterpolator(QWidget* parent, const char* /*name*/) :QWidget(parent), ACTION_TO_SLICEDIMENSION( createActionToSliceDimension() ), m_Interpolator( mitk::SegmentationInterpolationController::New() ), m_MultiWidget(NULL), m_ToolManager(NULL), m_Initialized(false), m_LastSliceDimension(2), m_LastSliceIndex(0), m_2DInterpolationEnabled(false), m_3DInterpolationEnabled(false) { m_SurfaceInterpolator = mitk::SurfaceInterpolationController::GetInstance(); QHBoxLayout* layout = new QHBoxLayout(this); m_GroupBoxEnableExclusiveInterpolationMode = new QGroupBox("Interpolation", this); QGridLayout* grid = new QGridLayout(m_GroupBoxEnableExclusiveInterpolationMode); m_RBtnEnable3DInterpolation = new QRadioButton("3D",this); connect(m_RBtnEnable3DInterpolation, SIGNAL(toggled(bool)), this, SLOT(On3DInterpolationEnabled(bool))); - connect(m_RBtnEnable3DInterpolation, SIGNAL(toggled(bool)), this, SIGNAL(Signal3DInterpolationEnabled(bool))); m_RBtnEnable3DInterpolation->setChecked(true); grid->addWidget(m_RBtnEnable3DInterpolation,0,0); m_BtnAccept3DInterpolation = new QPushButton("Accept", this); m_BtnAccept3DInterpolation->setEnabled(false); connect(m_BtnAccept3DInterpolation, SIGNAL(clicked()), this, SLOT(OnAccept3DInterpolationClicked())); grid->addWidget(m_BtnAccept3DInterpolation, 0,1); m_CbShowMarkers = new QCheckBox("Show Position Nodes", this); m_CbShowMarkers->setChecked(true); connect(m_CbShowMarkers, SIGNAL(toggled(bool)), this, SLOT(OnShowMarkers(bool))); connect(m_CbShowMarkers, SIGNAL(toggled(bool)), this, SIGNAL(SignalShowMarkerNodes(bool))); grid->addWidget(m_CbShowMarkers,0,2); m_RBtnEnable2DInterpolation = new QRadioButton("2D",this); connect(m_RBtnEnable2DInterpolation, SIGNAL(toggled(bool)), this, SLOT(On2DInterpolationEnabled(bool))); grid->addWidget(m_RBtnEnable2DInterpolation,1,0); m_BtnAcceptInterpolation = new QPushButton("Accept", this); m_BtnAcceptInterpolation->setEnabled( false ); connect( m_BtnAcceptInterpolation, SIGNAL(clicked()), this, SLOT(OnAcceptInterpolationClicked()) ); grid->addWidget(m_BtnAcceptInterpolation,1,1); m_BtnAcceptAllInterpolations = new QPushButton("... for all slices", this); m_BtnAcceptAllInterpolations->setEnabled( false ); connect( m_BtnAcceptAllInterpolations, SIGNAL(clicked()), this, SLOT(OnAcceptAllInterpolationsClicked()) ); grid->addWidget(m_BtnAcceptAllInterpolations,1,2); m_RBtnDisableInterpolation = new QRadioButton("Disable", this); connect(m_RBtnDisableInterpolation, SIGNAL(toggled(bool)), this, SLOT(OnInterpolationDisabled(bool))); grid->addWidget(m_RBtnDisableInterpolation, 2,0); layout->addWidget(m_GroupBoxEnableExclusiveInterpolationMode); this->setLayout(layout); itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnInterpolationInfoChanged ); InterpolationInfoChangedObserverTag = m_Interpolator->AddObserver( itk::ModifiedEvent(), command ); itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged ); SurfaceInterpolationInfoChangedObserverTag = m_SurfaceInterpolator->AddObserver( itk::ModifiedEvent(), command2 ); // feedback node and its visualization properties m_FeedbackNode = mitk::DataNode::New(); mitk::CoreObjectFactory::GetInstance()->SetDefaultProperties( m_FeedbackNode ); m_FeedbackNode->SetProperty( "binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "outline binary", mitk::BoolProperty::New(true) ); m_FeedbackNode->SetProperty( "color", mitk::ColorProperty::New(255.0, 255.0, 0.0) ); m_FeedbackNode->SetProperty( "texture interpolation", mitk::BoolProperty::New(false) ); m_FeedbackNode->SetProperty( "layer", mitk::IntProperty::New( 20 ) ); m_FeedbackNode->SetProperty( "levelwindow", mitk::LevelWindowProperty::New( mitk::LevelWindow(0, 1) ) ); m_FeedbackNode->SetProperty( "name", mitk::StringProperty::New("Interpolation feedback") ); m_FeedbackNode->SetProperty( "opacity", mitk::FloatProperty::New(0.8) ); m_FeedbackNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode = mitk::DataNode::New(); m_InterpolatedSurfaceNode->SetProperty( "color", mitk::ColorProperty::New(255.0,255.0,0.0) ); m_InterpolatedSurfaceNode->SetProperty( "name", mitk::StringProperty::New("Surface Interpolation feedback") ); m_InterpolatedSurfaceNode->SetProperty( "opacity", mitk::FloatProperty::New(0.5) ); m_InterpolatedSurfaceNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_InterpolatedSurfaceNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode = mitk::DataNode::New(); m_3DContourNode->SetProperty( "color", mitk::ColorProperty::New(0.0, 0.0, 0.0) ); m_3DContourNode->SetProperty("helper object", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "name", mitk::StringProperty::New("Drawn Contours") ); m_3DContourNode->SetProperty("material.representation", mitk::VtkRepresentationProperty::New(VTK_WIREFRAME)); m_3DContourNode->SetProperty("material.wireframeLineWidth", mitk::FloatProperty::New(2.0f)); m_3DContourNode->SetProperty("3DContourContainer", mitk::BoolProperty::New(true)); m_3DContourNode->SetProperty( "includeInBoundingBox", mitk::BoolProperty::New(false)); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget2"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3"))); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); QWidget::setContentsMargins(0, 0, 0, 0); if ( QWidget::layout() != NULL ) { QWidget::layout()->setContentsMargins(0, 0, 0, 0); } //For running 3D Interpolation in background // create a QFuture and a QFutureWatcher connect(&m_Watcher, SIGNAL(started()), this, SLOT(StartUpdateInterpolationTimer())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(SurfaceInterpolationFinished())); connect(&m_Watcher, SIGNAL(finished()), this, SLOT(StopUpdateInterpolationTimer())); m_Timer = new QTimer(this); connect(m_Timer, SIGNAL(timeout()), this, SLOT(ChangeSurfaceColor())); } void QmitkSlicesInterpolator::SetDataStorage( mitk::DataStorage& storage ) { m_DataStorage = &storage; m_SurfaceInterpolator->SetDataStorage(storage); } mitk::DataStorage* QmitkSlicesInterpolator::GetDataStorage() { if ( m_DataStorage.IsNotNull() ) { return m_DataStorage; } else { return NULL; } } void QmitkSlicesInterpolator::Initialize(mitk::ToolManager* toolManager, QmitkStdMultiWidget* multiWidget) { if (m_Initialized) { // remove old observers if (m_ToolManager) { m_ToolManager->WorkingDataChanged -= mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified ); m_ToolManager->ReferenceDataChanged -= mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified ); } if (m_MultiWidget) { disconnect( m_MultiWidget, SIGNAL(destroyed(QObject*)), this, SLOT(OnMultiWidgetDeleted(QObject*)) ); mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( TSliceObserverTag ); slicer->RemoveObserver( TTimeObserverTag ); slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( SSliceObserverTag ); slicer->RemoveObserver( STimeObserverTag ); slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( FSliceObserverTag ); slicer->RemoveObserver( FTimeObserverTag ); } //return; } m_MultiWidget = multiWidget; connect( m_MultiWidget, SIGNAL(destroyed(QObject*)), this, SLOT(OnMultiWidgetDeleted(QObject*)) ); m_ToolManager = toolManager; if (m_ToolManager) { // set enabled only if a segmentation is selected mitk::DataNode* node = m_ToolManager->GetWorkingData(0); QWidget::setEnabled( node != NULL ); // react whenever the set of selected segmentation changes m_ToolManager->WorkingDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerWorkingDataModified ); m_ToolManager->ReferenceDataChanged += mitk::MessageDelegate( this, &QmitkSlicesInterpolator::OnToolManagerReferenceDataModified ); // connect to the steppers of the three multi widget widgets. after each change, call the interpolator if (m_MultiWidget) { mitk::SliceNavigationController* slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); m_TimeStep.resize(3); m_TimeStep[2] = slicer->GetTime()->GetPos(); { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnTransversalTimeChanged ); TTimeObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(NULL, 0), command ); } { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnTransversalSliceChanged ); TSliceObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } // connect to the steppers of the three multi widget widgets. after each change, call the interpolator slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); m_TimeStep[0] = slicer->GetTime()->GetPos(); { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSagittalTimeChanged ); STimeObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(NULL, 0), command ); } { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnSagittalSliceChanged ); SSliceObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } // connect to the steppers of the three multi widget widgets. after each change, call the interpolator slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); m_TimeStep[1] = slicer->GetTime()->GetPos(); { itk::MemberCommand::Pointer command = itk::MemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnFrontalTimeChanged ); FTimeObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometryTimeEvent(NULL, 0), command ); } { itk::ReceptorMemberCommand::Pointer command = itk::ReceptorMemberCommand::New(); command->SetCallbackFunction( this, &QmitkSlicesInterpolator::OnFrontalSliceChanged ); FSliceObserverTag = slicer->AddObserver( mitk::SliceNavigationController::GeometrySliceEvent(NULL, 0), command ); } } } m_Initialized = true; } QmitkSlicesInterpolator::~QmitkSlicesInterpolator() { if (m_MultiWidget) { mitk::SliceNavigationController* slicer; if(m_MultiWidget->mitkWidget1 != NULL) { slicer = m_MultiWidget->mitkWidget1->GetSliceNavigationController(); slicer->RemoveObserver( TSliceObserverTag ); slicer->RemoveObserver( TTimeObserverTag ); } if(m_MultiWidget->mitkWidget2 != NULL) { slicer = m_MultiWidget->mitkWidget2->GetSliceNavigationController(); slicer->RemoveObserver( SSliceObserverTag ); slicer->RemoveObserver( STimeObserverTag ); } if(m_MultiWidget->mitkWidget3 != NULL) { slicer = m_MultiWidget->mitkWidget3->GetSliceNavigationController(); slicer->RemoveObserver( FSliceObserverTag ); slicer->RemoveObserver( FTimeObserverTag ); } } if(m_DataStorage->Exists(m_3DContourNode)) m_DataStorage->Remove(m_3DContourNode); if(m_DataStorage->Exists(m_InterpolatedSurfaceNode)) m_DataStorage->Remove(m_InterpolatedSurfaceNode); delete m_Timer; } void QmitkSlicesInterpolator::On2DInterpolationEnabled(bool status) { OnInterpolationActivated(status); } void QmitkSlicesInterpolator::On3DInterpolationEnabled(bool status) { On3DInterpolationActivated(status); } void QmitkSlicesInterpolator::OnInterpolationDisabled(bool status) { if (status) { OnInterpolationActivated(!status); On3DInterpolationActivated(!status); } } void QmitkSlicesInterpolator::OnShowMarkers(bool state) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = m_DataStorage->GetSubset(mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { it->Value()->SetProperty("helper object", mitk::BoolProperty::New(!state)); } } void QmitkSlicesInterpolator::OnToolManagerWorkingDataModified() { if (m_2DInterpolationEnabled) { OnInterpolationActivated( true ); // re-initialize if needed } if (m_3DInterpolationEnabled) { On3DInterpolationActivated( true); } } void QmitkSlicesInterpolator::OnToolManagerReferenceDataModified() { if (m_2DInterpolationEnabled) { OnInterpolationActivated( true ); // re-initialize if needed } if (m_3DInterpolationEnabled) { m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); } } void QmitkSlicesInterpolator::OnTransversalTimeChanged(itk::Object* sender, const itk::EventObject& e) { const mitk::SliceNavigationController::GeometryTimeEvent& event = dynamic_cast(e); m_TimeStep[2] = event.GetPos(); if (m_LastSliceDimension == 2) { mitk::SliceNavigationController* snc = dynamic_cast( sender ); if (snc) snc->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnSagittalTimeChanged(itk::Object* sender, const itk::EventObject& e) { const mitk::SliceNavigationController::GeometryTimeEvent& event = dynamic_cast(e); m_TimeStep[0] = event.GetPos(); if (m_LastSliceDimension == 0) { mitk::SliceNavigationController* snc = dynamic_cast( sender ); if (snc) snc->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnFrontalTimeChanged(itk::Object* sender, const itk::EventObject& e) { const mitk::SliceNavigationController::GeometryTimeEvent& event = dynamic_cast(e); m_TimeStep[1] = event.GetPos(); if (m_LastSliceDimension == 1) { mitk::SliceNavigationController* snc = dynamic_cast( sender ); if (snc) snc->SendSlice(); // will trigger a new interpolation } } void QmitkSlicesInterpolator::OnTransversalSliceChanged(const itk::EventObject& e) { if ( TranslateAndInterpolateChangedSlice( e, 2 ) ) { if (m_MultiWidget) { mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget1->GetRenderWindow())->RequestUpdate(); } } } void QmitkSlicesInterpolator::OnSagittalSliceChanged(const itk::EventObject& e) { if ( TranslateAndInterpolateChangedSlice( e, 0 ) ) { if (m_MultiWidget) { mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget2->GetRenderWindow())->RequestUpdate(); } } } void QmitkSlicesInterpolator::OnFrontalSliceChanged(const itk::EventObject& e) { if ( TranslateAndInterpolateChangedSlice( e, 1 ) ) { if (m_MultiWidget) { mitk::BaseRenderer::GetInstance(m_MultiWidget->mitkWidget3->GetRenderWindow())->RequestUpdate(); } } } bool QmitkSlicesInterpolator::TranslateAndInterpolateChangedSlice(const itk::EventObject& e, unsigned int windowID) { if (!m_2DInterpolationEnabled) return false; try { const mitk::SliceNavigationController::GeometrySliceEvent& event = dynamic_cast(e); mitk::TimeSlicedGeometry* tsg = event.GetTimeSlicedGeometry(); if (tsg && m_TimeStep.size() > windowID) { mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(tsg->GetGeometry3D(m_TimeStep[windowID])); if (slicedGeometry) { mitk::PlaneGeometry* plane = dynamic_cast(slicedGeometry->GetGeometry2D( event.GetPos() )); if (plane) Interpolate( plane, m_TimeStep[windowID] ); return true; } } } catch(std::bad_cast) { return false; // so what } return false; } void QmitkSlicesInterpolator::Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep ) { if (m_ToolManager) { mitk::DataNode* node = m_ToolManager->GetWorkingData(0); if (node) { m_Segmentation = dynamic_cast(node->GetData()); if (m_Segmentation) { int clickedSliceDimension(-1); int clickedSliceIndex(-1); // calculate real slice position, i.e. slice of the image and not slice of the TimeSlicedGeometry mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, plane, clickedSliceDimension, clickedSliceIndex ); mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( clickedSliceDimension, clickedSliceIndex, timeStep ); m_FeedbackNode->SetData( interpolation ); // Workaround for Bug 11318 if ((interpolation.IsNotNull()) && (interpolation->GetGeometry() != NULL)) { if(clickedSliceDimension == 1) { mitk::Point3D orig = interpolation->GetGeometry()->GetOrigin(); orig[0] = orig[0]; orig[1] = orig[1] + 0.5; orig[2] = orig[2]; interpolation->GetGeometry()->SetOrigin(orig); } } // Workaround for Bug 11318 END m_LastSliceDimension = clickedSliceDimension; m_LastSliceIndex = clickedSliceIndex; } } } } void QmitkSlicesInterpolator::SurfaceInterpolationFinished() { mitk::Surface::Pointer interpolatedSurface = m_SurfaceInterpolator->GetInterpolationResult(); if(interpolatedSurface.IsNotNull()) { m_BtnAccept3DInterpolation->setEnabled(true); m_InterpolatedSurfaceNode->SetData(interpolatedSurface); m_3DContourNode->SetData(m_SurfaceInterpolator->GetContoursAsSurface()); m_InterpolatedSurfaceNode->SetVisibility(true); m_3DContourNode->SetVisibility(true, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); if( !m_DataStorage->Exists(m_InterpolatedSurfaceNode) && !m_DataStorage->Exists(m_3DContourNode)) { m_DataStorage->Add(m_3DContourNode); m_DataStorage->Add(m_InterpolatedSurfaceNode); } } else if (interpolatedSurface.IsNull()) { m_BtnAccept3DInterpolation->setEnabled(false); if (m_DataStorage->Exists(m_InterpolatedSurfaceNode)) { m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); } } if (m_MultiWidget) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::OnAcceptInterpolationClicked() { if (m_Segmentation && m_FeedbackNode->GetData()) { //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); mitk::OverwriteSliceImageFilter::Pointer slicewriter = mitk::OverwriteSliceImageFilter::New(); slicewriter->SetInput( m_Segmentation ); slicewriter->SetCreateUndoInformation( true ); slicewriter->SetSliceImage( dynamic_cast(m_FeedbackNode->GetData()) ); slicewriter->SetSliceDimension( m_LastSliceDimension ); slicewriter->SetSliceIndex( m_LastSliceIndex ); slicewriter->SetTimeStep( m_TimeStep[m_LastSliceDimension] ); slicewriter->Update(); m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::AcceptAllInterpolations(unsigned int windowID) { // first creates a 3D diff image, then applies this diff to the segmentation if (m_Segmentation) { int sliceDimension(-1); int dummySliceIndex(-1); if (!GetSliceForWindowsID(windowID, sliceDimension, dummySliceIndex)) { return; // cannot determine slice orientation } //making interpolation separately undoable mitk::UndoStackItem::IncCurrObjectEventId(); mitk::UndoStackItem::IncCurrGroupEventId(); mitk::UndoStackItem::ExecuteIncrement(); // create a diff image for the undo operation mitk::Image::Pointer diffImage = mitk::Image::New(); diffImage->Initialize( m_Segmentation ); mitk::PixelType pixelType( mitk::MakeScalarPixelType() ); diffImage->Initialize( pixelType, 3, m_Segmentation->GetDimensions() ); memset( diffImage->GetData(), 0, (pixelType.GetBpe() >> 3) * diffImage->GetDimension(0) * diffImage->GetDimension(1) * diffImage->GetDimension(2) ); // now the diff image is all 0 unsigned int timeStep( m_TimeStep[windowID] ); // a slicewriter to create the diff image mitk::OverwriteSliceImageFilter::Pointer diffslicewriter = mitk::OverwriteSliceImageFilter::New(); diffslicewriter->SetCreateUndoInformation( false ); diffslicewriter->SetInput( diffImage ); diffslicewriter->SetSliceDimension( sliceDimension ); diffslicewriter->SetTimeStep( timeStep ); unsigned int totalChangedSlices(0); unsigned int zslices = m_Segmentation->GetDimension( sliceDimension ); mitk::ProgressBar::GetInstance()->AddStepsToDo(zslices); for (unsigned int sliceIndex = 0; sliceIndex < zslices; ++sliceIndex) { mitk::Image::Pointer interpolation = m_Interpolator->Interpolate( sliceDimension, sliceIndex, timeStep ); if (interpolation.IsNotNull()) // we don't check if interpolation is necessary/sensible - but m_Interpolator does { diffslicewriter->SetSliceImage( interpolation ); diffslicewriter->SetSliceIndex( sliceIndex ); diffslicewriter->Update(); ++totalChangedSlices; } mitk::ProgressBar::GetInstance()->Progress(); } if (totalChangedSlices > 0) { // store undo stack items if ( true ) { // create do/undo operations (we don't execute the doOp here, because it has already been executed during calculation of the diff image mitk::ApplyDiffImageOperation* doOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); mitk::ApplyDiffImageOperation* undoOp = new mitk::ApplyDiffImageOperation( mitk::OpTEST, m_Segmentation, diffImage, timeStep ); undoOp->SetFactor( -1.0 ); std::stringstream comment; comment << "Accept all interpolations (" << totalChangedSlices << ")"; mitk::OperationEvent* undoStackItem = new mitk::OperationEvent( mitk::DiffImageApplier::GetInstanceForUndo(), doOp, undoOp, comment.str() ); mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); // acutally apply the changes here mitk::DiffImageApplier::GetInstanceForUndo()->ExecuteOperation( doOp ); } } m_FeedbackNode->SetData(NULL); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::FinishInterpolation(int windowID) { //this redirect is for calling from outside if (windowID < 0) OnAcceptAllInterpolationsClicked(); else AcceptAllInterpolations( (unsigned int)windowID ); } void QmitkSlicesInterpolator::OnAcceptAllInterpolationsClicked() { QMenu orientationPopup(this); std::map::const_iterator it; for(it = ACTION_TO_SLICEDIMENSION.begin(); it != ACTION_TO_SLICEDIMENSION.end(); it++) orientationPopup.addAction(it->first); connect( &orientationPopup, SIGNAL(triggered(QAction*)), this, SLOT(OnAcceptAllPopupActivated(QAction*)) ); orientationPopup.exec( QCursor::pos() ); } void QmitkSlicesInterpolator::OnAccept3DInterpolationClicked() { if (m_InterpolatedSurfaceNode.IsNotNull() && m_InterpolatedSurfaceNode->GetData()) { mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(dynamic_cast(m_InterpolatedSurfaceNode->GetData())); s2iFilter->SetImage(dynamic_cast(m_ToolManager->GetReferenceData(0)->GetData())); s2iFilter->Update(); mitk::DataNode* segmentationNode = m_ToolManager->GetWorkingData(0); segmentationNode->SetData(s2iFilter->GetOutput()); m_RBtnDisableInterpolation->setChecked(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSlicesInterpolator::OnAcceptAllPopupActivated(QAction* action) { try { std::map::const_iterator iter = ACTION_TO_SLICEDIMENSION.find( action ); if (iter != ACTION_TO_SLICEDIMENSION.end()) { int windowID = iter->second; AcceptAllInterpolations( windowID ); } } catch(...) { /* Showing message box with possible memory error */ QMessageBox errorInfo; errorInfo.setWindowTitle("Interpolation Process"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during interpolation. Possible cause: Not enough memory!"); errorInfo.exec(); //additional error message on std::cerr std::cerr << "Ill construction in " __FILE__ " l. " << __LINE__ << std::endl; } } void QmitkSlicesInterpolator::OnInterpolationActivated(bool on) { m_2DInterpolationEnabled = on; try { if ( m_DataStorage.IsNotNull() ) { if (on && !m_DataStorage->Exists(m_FeedbackNode)) { m_DataStorage->Add( m_FeedbackNode ); } //else //{ // m_DataStorage->Remove( m_FeedbackNode ); //} } } catch(...) { // don't care (double add/remove) } if (m_ToolManager) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); QWidget::setEnabled( workingNode != NULL ); m_BtnAcceptAllInterpolations->setEnabled( on ); m_BtnAcceptInterpolation->setEnabled( on ); m_FeedbackNode->SetVisibility( on ); if (!on) { mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } if (workingNode) { mitk::Image* segmentation = dynamic_cast(workingNode->GetData()); if (segmentation) { m_Interpolator->SetSegmentationVolume( segmentation ); if (referenceNode) { mitk::Image* referenceImage = dynamic_cast(referenceNode->GetData()); m_Interpolator->SetReferenceVolume( referenceImage ); // may be NULL } } } } UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::Run3DInterpolation() { m_SurfaceInterpolator->Interpolate(); } void QmitkSlicesInterpolator::StartUpdateInterpolationTimer() { m_Timer->start(500); } void QmitkSlicesInterpolator::StopUpdateInterpolationTimer() { m_Timer->stop(); m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,0.0)); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::ChangeSurfaceColor() { float currentColor[3]; m_InterpolatedSurfaceNode->GetColor(currentColor); float yellow[3] = {255.0,255.0,0.0}; if( currentColor[2] == yellow[2]) { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(255.0,255.0,255.0)); } else { m_InterpolatedSurfaceNode->SetProperty("color", mitk::ColorProperty::New(yellow)); } m_InterpolatedSurfaceNode->Update(); mitk::RenderingManager::GetInstance()->RequestUpdate(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))->GetRenderWindow()); } void QmitkSlicesInterpolator::On3DInterpolationActivated(bool on) { m_3DInterpolationEnabled = on; try { if ( m_DataStorage.IsNotNull() && m_ToolManager && m_3DInterpolationEnabled) { mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); if (workingNode) { int listID; bool isInterpolationResult(false); workingNode->GetBoolProperty("3DInterpolationResult",isInterpolationResult); if ((workingNode->IsSelected() && workingNode->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget3")))) && !isInterpolationResult) { if (workingNode->GetIntProperty("3DInterpolationListID", listID)) { m_SurfaceInterpolator->SetCurrentListID(listID); if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } else { listID = m_SurfaceInterpolator->CreateNewContourList(); workingNode->SetIntProperty("3DInterpolationListID", listID); m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_BtnAccept3DInterpolation->setEnabled(false); } mitk::Vector3D spacing = workingNode->GetData()->GetGeometry( m_MultiWidget->GetRenderWindow3()->GetRenderer()->GetTimeStep() )->GetSpacing(); double minSpacing (100); double maxSpacing (0); for (int i =0; i < 3; i++) { if (spacing[i] < minSpacing) { minSpacing = spacing[i]; } else if (spacing[i] > maxSpacing) { maxSpacing = spacing[i]; } } m_SurfaceInterpolator->SetWorkingImage(dynamic_cast(workingNode->GetData())); m_SurfaceInterpolator->SetMaxSpacing(maxSpacing); m_SurfaceInterpolator->SetMinSpacing(minSpacing); m_SurfaceInterpolator->SetDistanceImageVolume(50000); } } QWidget::setEnabled( workingNode != NULL ); m_CbShowMarkers->setEnabled(m_3DInterpolationEnabled); } else if (!m_3DInterpolationEnabled) { m_InterpolatedSurfaceNode->SetVisibility(false); m_3DContourNode->SetVisibility(false, mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget4"))); m_BtnAccept3DInterpolation->setEnabled(m_3DInterpolationEnabled); } } catch(...) { MITK_ERROR<<"Error with 3D surface interpolation!"; } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::EnableInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated OnInterpolationActivated(on); } void QmitkSlicesInterpolator::Enable3DInterpolation(bool on) { // only to be called from the outside world // just a redirection to OnInterpolationActivated On3DInterpolationActivated(on); } void QmitkSlicesInterpolator::UpdateVisibleSuggestion() { if (m_2DInterpolationEnabled) { // determine which one is the current view, try to do an initial interpolation mitk::BaseRenderer* renderer = mitk::GlobalInteraction::GetInstance()->GetFocus(); if (renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { const mitk::TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast( renderer->GetWorldGeometry() ); if (timeSlicedGeometry) { mitk::SliceNavigationController::GeometrySliceEvent event( const_cast(timeSlicedGeometry), renderer->GetSlice() ); if ( renderer->GetCurrentWorldGeometry2DNode() ) { if ( renderer->GetCurrentWorldGeometry2DNode()==this->m_MultiWidget->GetWidgetPlane1() ) { TranslateAndInterpolateChangedSlice( event, 2 ); } else if ( renderer->GetCurrentWorldGeometry2DNode()==this->m_MultiWidget->GetWidgetPlane2() ) { TranslateAndInterpolateChangedSlice( event, 0 ); } else if ( renderer->GetCurrentWorldGeometry2DNode()==this->m_MultiWidget->GetWidgetPlane3() ) { TranslateAndInterpolateChangedSlice( event, 1 ); } } } } } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSlicesInterpolator::OnInterpolationInfoChanged(const itk::EventObject& /*e*/) { // something (e.g. undo) changed the interpolation info, we should refresh our display UpdateVisibleSuggestion(); } void QmitkSlicesInterpolator::OnSurfaceInterpolationInfoChanged(const itk::EventObject& /*e*/) { if(m_3DInterpolationEnabled) { if (m_Watcher.isRunning()) m_Watcher.waitForFinished(); m_Future = QtConcurrent::run(this, &QmitkSlicesInterpolator::Run3DInterpolation); m_Watcher.setFuture(m_Future); } } bool QmitkSlicesInterpolator::GetSliceForWindowsID(unsigned windowID, int& sliceDimension, int& sliceIndex) { mitk::BaseRenderer* renderer(NULL); // find sliceDimension for windowID: // windowID 2: transversal window = renderWindow1 // windowID 1: frontal window = renderWindow3 // windowID 0: sagittal window = renderWindow2 if ( m_MultiWidget ) { switch (windowID) { case 2: default: renderer = m_MultiWidget->mitkWidget1->GetRenderer(); break; case 1: renderer = m_MultiWidget->mitkWidget3->GetRenderer(); break; case 0: renderer = m_MultiWidget->mitkWidget2->GetRenderer(); break; } } if ( m_Segmentation && renderer && renderer->GetMapperID() == mitk::BaseRenderer::Standard2D) { const mitk::TimeSlicedGeometry* timeSlicedGeometry = dynamic_cast( renderer->GetWorldGeometry() ); if (timeSlicedGeometry) { mitk::SlicedGeometry3D* slicedGeometry = dynamic_cast(timeSlicedGeometry->GetGeometry3D(m_TimeStep[windowID])); if (slicedGeometry) { mitk::PlaneGeometry* plane = dynamic_cast(slicedGeometry->GetGeometry2D( renderer->GetSlice() )); Interpolate( plane, m_TimeStep[windowID] ); return mitk::SegTool2D::DetermineAffectedImageSlice( m_Segmentation, plane, sliceDimension, sliceIndex ); } } } return false; } void QmitkSlicesInterpolator::OnMultiWidgetDeleted(QObject*) { if (m_MultiWidget) { m_MultiWidget = NULL; } } diff --git a/Modules/QmitkExt/QmitkSlicesInterpolator.h b/Modules/QmitkExt/QmitkSlicesInterpolator.h index 1fe87695a5..c049dbc48c 100644 --- a/Modules/QmitkExt/QmitkSlicesInterpolator.h +++ b/Modules/QmitkExt/QmitkSlicesInterpolator.h @@ -1,300 +1,299 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkSlicesInterpolator_h_Included #define QmitkSlicesInterpolator_h_Included #include "mitkSliceNavigationController.h" #include "QmitkExtExports.h" #include "mitkSegmentationInterpolationController.h" #include "mitkDataNode.h" #include "mitkDataStorage.h" #include "mitkWeakPointer.h" #include "mitkSurfaceInterpolationController.h" #include #include #include #include #include #include "mitkVtkRepresentationProperty.h" #include "vtkProperty.h" //For running 3D interpolation in background #include #include #include #include namespace mitk { class ToolManager; class PlaneGeometry; } class QmitkStdMultiWidget; class QPushButton; //Enhancement for 3D Interpolation //class QRadioButton; //class QGroupBox; //class QCheckBox; /** \brief GUI for slices interpolation. \ingroup ToolManagerEtAl \ingroup Widgets \sa QmitkInteractiveSegmentation \sa mitk::SegmentationInterpolation There is a separate page describing the general design of QmitkInteractiveSegmentation: \ref QmitkInteractiveSegmentationTechnicalPage While mitk::SegmentationInterpolation does the bookkeeping of interpolation (keeping track of which slices contain how much segmentation) and the algorithmic work, QmitkSlicesInterpolator is responsible to watch the GUI, to notice, which slice is currently visible. It triggers generation of interpolation suggestions and also triggers acception of suggestions. \todo show/hide feedback on demand Last contributor: $Author: maleike $ */ class QmitkExt_EXPORT QmitkSlicesInterpolator : public QWidget { Q_OBJECT public: QmitkSlicesInterpolator(QWidget* parent = 0, const char* name = 0); /** To be called once before real use. */ void Initialize(mitk::ToolManager* toolManager, QmitkStdMultiWidget* multiWidget); virtual ~QmitkSlicesInterpolator(); void SetDataStorage( mitk::DataStorage& storage ); mitk::DataStorage* GetDataStorage(); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnToolManagerWorkingDataModified(); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnToolManagerReferenceDataModified(); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnTransversalTimeChanged(itk::Object* sender, const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnSagittalTimeChanged(itk::Object* sender, const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnFrontalTimeChanged(itk::Object* sender, const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnTransversalSliceChanged(const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnSagittalSliceChanged(const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnFrontalSliceChanged(const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnInterpolationInfoChanged(const itk::EventObject&); /** Just public because it is called by itk::Commands. You should not need to call this. */ void OnSurfaceInterpolationInfoChanged(const itk::EventObject&); signals: void SignalRememberContourPositions(bool); void SignalShowMarkerNodes(bool); - void Signal3DInterpolationEnabled(bool); public slots: /** Call this from the outside to enable/disable interpolation */ void EnableInterpolation(bool); void Enable3DInterpolation(bool); /** Call this from the outside to accept all interpolations */ void FinishInterpolation(int windowID = -1); protected slots: /** Reaction to button clicks. */ void OnAcceptInterpolationClicked(); /* Opens popup to ask about which orientation should be interpolated */ void OnAcceptAllInterpolationsClicked(); /* Reaction to button clicks */ void OnAccept3DInterpolationClicked(); /* * Will trigger interpolation for all slices in given orientation (called from popup menu of OnAcceptAllInterpolationsClicked) */ void OnAcceptAllPopupActivated(QAction* action); /** Called on activation/deactivation */ void OnInterpolationActivated(bool); void On3DInterpolationActivated(bool); void OnMultiWidgetDeleted(QObject*); //Enhancement for 3D interpolation void On2DInterpolationEnabled(bool); void On3DInterpolationEnabled(bool); void OnInterpolationDisabled(bool); void OnShowMarkers(bool); void Run3DInterpolation(); void SurfaceInterpolationFinished(); void StartUpdateInterpolationTimer(); void StopUpdateInterpolationTimer(); void ChangeSurfaceColor(); protected: const std::map createActionToSliceDimension(); const std::map ACTION_TO_SLICEDIMENSION; void AcceptAllInterpolations(unsigned int windowID); /** Retrieves the currently selected PlaneGeometry from a SlicedGeometry3D that is generated by a SliceNavigationController and calls Interpolate to further process this PlaneGeometry into an interpolation. \param e is a actually a mitk::SliceNavigationController::GeometrySliceEvent, sent by a SliceNavigationController \param windowID is 2 for transversal, 1 for frontal, 0 for sagittal (similar to sliceDimension in other methods) */ bool TranslateAndInterpolateChangedSlice(const itk::EventObject& e, unsigned int windowID); /** Given a PlaneGeometry, this method figures out which slice of the first working image (of the associated ToolManager) should be interpolated. The actual work is then done by our SegmentationInterpolation object. */ void Interpolate( mitk::PlaneGeometry* plane, unsigned int timeStep ); //void InterpolateSurface(); /** Called internally to update the interpolation suggestion. Finds out about the focused render window and requests an interpolation. */ void UpdateVisibleSuggestion(); /** * Tries to figure out the slice position and orientation for a given render window. * \param windowID is 2 for transversal, 1 for frontal, 0 for sagittal (similar to sliceDimension in other methods) * \return false if orientation could not be determined */ bool GetSliceForWindowsID(unsigned windowID, int& sliceDimension, int& sliceIndex); mitk::SegmentationInterpolationController::Pointer m_Interpolator; mitk::SurfaceInterpolationController::Pointer m_SurfaceInterpolator; QmitkStdMultiWidget* m_MultiWidget; mitk::ToolManager* m_ToolManager; bool m_Initialized; unsigned int TSliceObserverTag; unsigned int SSliceObserverTag; unsigned int FSliceObserverTag; unsigned int TTimeObserverTag; unsigned int STimeObserverTag; unsigned int FTimeObserverTag; unsigned int InterpolationInfoChangedObserverTag; unsigned int SurfaceInterpolationInfoChangedObserverTag; QPushButton* m_BtnAcceptInterpolation; QPushButton* m_BtnAcceptAllInterpolations; //Enhancement for 3D Surface Interpolation QRadioButton* m_RBtnEnable2DInterpolation; QRadioButton* m_RBtnEnable3DInterpolation; QRadioButton* m_RBtnDisableInterpolation; QGroupBox* m_GroupBoxEnableExclusiveInterpolationMode; QPushButton* m_BtnAccept3DInterpolation; QCheckBox* m_CbShowMarkers; mitk::DataNode::Pointer m_FeedbackNode; mitk::DataNode::Pointer m_InterpolatedSurfaceNode; mitk::DataNode::Pointer m_3DContourNode; mitk::Image* m_Segmentation; unsigned int m_LastSliceDimension; unsigned int m_LastSliceIndex; std::vector m_TimeStep; // current time step of the render windows bool m_2DInterpolationEnabled; bool m_3DInterpolationEnabled; //unsigned int m_CurrentListID; mitk::WeakPointer m_DataStorage; QFuture m_Future; QFutureWatcher m_Watcher; QTimer* m_Timer; }; #endif diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp index 8e5433a2c5..ad0db3d800 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.cpp +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.cpp @@ -1,393 +1,384 @@ /*=================================================================== 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 "mitkSegTool2D.h" #include "mitkToolManager.h" #include "mitkDataStorage.h" #include "mitkBaseRenderer.h" #include "mitkPlaneGeometry.h" #include "mitkExtractImageFilter.h" #include "mitkExtractDirectedPlaneImageFilter.h" //Include of the new ImageExtractor #include "mitkExtractDirectedPlaneImageFilterNew.h" #include "mitkPlanarCircle.h" #include "mitkOverwriteSliceImageFilter.h" #include "mitkOverwriteDirectedPlaneImageFilter.h" #include "mitkGetModuleContext.h" //Includes for 3DSurfaceInterpolation #include "mitkImageToContourFilter.h" #include "mitkSurfaceInterpolationController.h" //includes for resling and overwriting #include #include #include #include #include #include "mitkOperationEvent.h" #include "mitkUndoController.h" #define ROUND(a) ((a)>0 ? (int)((a)+0.5) : -(int)(0.5-(a))) mitk::SegTool2D::SegTool2D(const char* type) :Tool(type), m_LastEventSender(NULL), m_LastEventSlice(0), m_Contourmarkername ("Position"), - m_ShowMarkerNodes (true), - m_3DInterpolationEnabled (true) + m_ShowMarkerNodes (true) { // great magic numbers CONNECT_ACTION( 80, OnMousePressed ); CONNECT_ACTION( 90, OnMouseMoved ); CONNECT_ACTION( 42, OnMouseReleased ); CONNECT_ACTION( 49014, OnInvertLogic ); } mitk::SegTool2D::~SegTool2D() { } bool mitk::SegTool2D::OnMousePressed (Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( positionEvent->GetSender()->GetMapperID() != BaseRenderer::Standard2D ) return false; // we don't want anything but 2D m_LastEventSender = positionEvent->GetSender(); m_LastEventSlice = m_LastEventSender->GetSlice(); return true; } bool mitk::SegTool2D::OnMouseMoved (Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::OnMouseReleased(Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::OnInvertLogic(Action*, const StateEvent* stateEvent) { const PositionEvent* positionEvent = dynamic_cast(stateEvent->GetEvent()); if (!positionEvent) return false; if ( m_LastEventSender != positionEvent->GetSender() ) return false; if ( m_LastEventSlice != m_LastEventSender->GetSlice() ) return false; return true; } bool mitk::SegTool2D::DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ) { assert(image); assert(plane); // compare normal of plane to the three axis vectors of the image Vector3D normal = plane->GetNormal(); Vector3D imageNormal0 = image->GetSlicedGeometry()->GetAxisVector(0); Vector3D imageNormal1 = image->GetSlicedGeometry()->GetAxisVector(1); Vector3D imageNormal2 = image->GetSlicedGeometry()->GetAxisVector(2); normal.Normalize(); imageNormal0.Normalize(); imageNormal1.Normalize(); imageNormal2.Normalize(); imageNormal0.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal0.Get_vnl_vector()) ); imageNormal1.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal1.Get_vnl_vector()) ); imageNormal2.Set_vnl_vector( vnl_cross_3d(normal.Get_vnl_vector(),imageNormal2.Get_vnl_vector()) ); double eps( 0.00001 ); // transversal if ( imageNormal2.GetNorm() <= eps ) { affectedDimension = 2; } // sagittal else if ( imageNormal1.GetNorm() <= eps ) { affectedDimension = 1; } // frontal else if ( imageNormal0.GetNorm() <= eps ) { affectedDimension = 0; } else { affectedDimension = -1; // no idea return false; } // determine slice number in image Geometry3D* imageGeometry = image->GetGeometry(0); Point3D testPoint = imageGeometry->GetCenter(); Point3D projectedPoint; plane->Project( testPoint, projectedPoint ); Point3D indexPoint; imageGeometry->WorldToIndex( projectedPoint, indexPoint ); affectedSlice = ROUND( indexPoint[affectedDimension] ); MITK_DEBUG << "indexPoint " << indexPoint << " affectedDimension " << affectedDimension << " affectedSlice " << affectedSlice; // check if this index is still within the image if ( affectedSlice < 0 || affectedSlice >= static_cast(image->GetDimension(affectedDimension)) ) return false; return true; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedImageSliceAs2DImage(const PositionEvent* positionEvent, const Image* image) { if (!positionEvent) return NULL; assert( positionEvent->GetSender() ); // sure, right? unsigned int timeStep = positionEvent->GetSender()->GetTimeStep( image ); // get the timestep of the visible part (time-wise) of the image // first, we determine, which slice is affected const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); if ( !image || !planeGeometry ) return NULL; //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); //set to false to extract a slice reslice->SetOverwriteMode(false); reslice->Modified(); //use ExtractSliceFilter with our specific vtkImageReslice for overwriting and extracting mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( image ); extractor->SetTimeStep( timeStep ); extractor->SetWorldGeometry( planeGeometry ); extractor->SetVtkOutputRequest(false); extractor->SetResliceTransformByGeometry( image->GetTimeSlicedGeometry()->GetGeometry3D( timeStep ) ); extractor->Modified(); extractor->Update(); Image::Pointer slice = extractor->GetOutput(); /*============= BEGIN undo feature block ========================*/ //specify the undo operation with the non edited slice m_undoOperation = new DiffSliceOperation(const_cast(image), extractor->GetVtkOutput(), slice->GetGeometry(), timeStep, const_cast(planeGeometry)); /*============= END undo feature block ========================*/ return slice; } mitk::Image::Pointer mitk::SegTool2D::GetAffectedWorkingSlice(const PositionEvent* positionEvent) { DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); if ( !workingNode ) return NULL; Image* workingImage = dynamic_cast(workingNode->GetData()); if ( !workingImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, workingImage ); } mitk::Image::Pointer mitk::SegTool2D::GetAffectedReferenceSlice(const PositionEvent* positionEvent) { DataNode* referenceNode( m_ToolManager->GetReferenceData(0) ); if ( !referenceNode ) return NULL; Image* referenceImage = dynamic_cast(referenceNode->GetData()); if ( !referenceImage ) return NULL; return GetAffectedImageSliceAs2DImage( positionEvent, referenceImage ); } void mitk::SegTool2D::WriteBackSegmentationResult (const PositionEvent* positionEvent, Image* slice) { const PlaneGeometry* planeGeometry( dynamic_cast (positionEvent->GetSender()->GetCurrentWorldGeometry2D() ) ); DataNode* workingNode( m_ToolManager->GetWorkingData(0) ); Image* image = dynamic_cast(workingNode->GetData()); //Make sure that for reslicing and overwriting the same alogrithm is used. We can specify the mode of the vtk reslicer vtkSmartPointer reslice = vtkSmartPointer::New(); //Set the slice as 'input' reslice->SetInputSlice(slice->GetVtkImageData(this->m_TimeStep)); //set overwrite mode to true to write back to the image volume reslice->SetOverwriteMode(true); reslice->Modified(); mitk::ExtractSliceFilter::Pointer extractor = mitk::ExtractSliceFilter::New(reslice); extractor->SetInput( image ); extractor->SetTimeStep( this->m_TimeStep ); extractor->SetWorldGeometry( planeGeometry ); extractor->SetVtkOutputRequest(true); extractor->SetResliceTransformByGeometry( image->GetTimeSlicedGeometry()->GetGeometry3D( this->m_TimeStep ) ); extractor->Modified(); extractor->Update(); //the image was modified within the pipeline, but not marked so image->Modified(); /*============= BEGIN undo feature block ========================*/ //specify the undo operation with the edited slice m_doOperation = new DiffSliceOperation(image, extractor->GetVtkOutput(),slice->GetGeometry(), this->m_TimeStep, const_cast(planeGeometry)); //create an operation event for the undo stack OperationEvent* undoStackItem = new OperationEvent( DiffSliceOperationApplier::GetInstance(), m_doOperation, m_undoOperation, "Segmentation" ); //add it to the undo controller UndoController::GetCurrentUndoModel()->SetOperationEvent( undoStackItem ); //clear the pointers as the operation are stored in the undocontroller and also deleted from there m_undoOperation = NULL; m_doOperation = NULL; /*============= END undo feature block ========================*/ - if ( m_3DInterpolationEnabled ) - { - slice->DisconnectPipeline(); - ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); - contourExtractor->SetInput(slice); - contourExtractor->Update(); - mitk::Surface::Pointer contour = contourExtractor->GetOutput(); - - if (contour->GetVtkPolyData()->GetNumberOfPoints() > 0 ) - { - unsigned int pos = this->AddContourmarker(positionEvent); - mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); - PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); - mitk::SurfaceInterpolationController::GetInstance()->AddNewContour( contour, service->GetPlanePosition(pos)); - contour->DisconnectPipeline(); - } + slice->DisconnectPipeline(); + ImageToContourFilter::Pointer contourExtractor = ImageToContourFilter::New(); + contourExtractor->SetInput(slice); + contourExtractor->Update(); + mitk::Surface::Pointer contour = contourExtractor->GetOutput(); + if (contour->GetVtkPolyData()->GetNumberOfPoints() > 0 ) + { + unsigned int pos = this->AddContourmarker(positionEvent); + mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); + PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); + mitk::SurfaceInterpolationController::GetInstance()->AddNewContour( contour, service->GetPlanePosition(pos)); + contour->DisconnectPipeline(); } + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::SegTool2D::SetShowMarkerNodes(bool status) { m_ShowMarkerNodes = status; } -void mitk::SegTool2D::Enable3DInterpolation(bool status) -{ - m_3DInterpolationEnabled = status; -} - unsigned int mitk::SegTool2D::AddContourmarker ( const PositionEvent* positionEvent ) { const mitk::Geometry2D* plane = dynamic_cast (dynamic_cast< const mitk::SlicedGeometry3D*>( positionEvent->GetSender()->GetSliceNavigationController()->GetCurrentGeometry3D())->GetGeometry2D(0)); mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); PlanePositionManagerService* service = dynamic_cast(mitk::GetModuleContext()->GetService(serviceRef)); unsigned int size = service->GetNumberOfPlanePositions(); unsigned int id = service->AddNewPlanePosition(plane, positionEvent->GetSender()->GetSliceNavigationController()->GetSlice()->GetPos()); mitk::PlanarCircle::Pointer contourMarker = mitk::PlanarCircle::New(); contourMarker->SetGeometry2D( const_cast(plane)); std::stringstream markerStream; mitk::DataNode* workingNode (m_ToolManager->GetWorkingData(0)); markerStream << m_Contourmarkername ; markerStream << " "; markerStream << id+1; DataNode::Pointer rotatedContourNode = DataNode::New(); rotatedContourNode->SetData(contourMarker); rotatedContourNode->SetProperty( "name", StringProperty::New(markerStream.str()) ); rotatedContourNode->SetProperty( "isContourMarker", BoolProperty::New(true)); rotatedContourNode->SetBoolProperty( "PlanarFigureInitializedWindow", true, positionEvent->GetSender() ); rotatedContourNode->SetProperty( "includeInBoundingBox", BoolProperty::New(false)); rotatedContourNode->SetProperty( "helper object", mitk::BoolProperty::New(!m_ShowMarkerNodes)); if (plane) { if ( id == size ) { m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } else { mitk::NodePredicateProperty::Pointer isMarker = mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true)); mitk::DataStorage::SetOfObjects::ConstPointer markers = m_ToolManager->GetDataStorage()->GetDerivations(workingNode,isMarker); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = markers->begin(); iter != markers->end(); ++iter) { std::string nodeName = (*iter)->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int markerId = atof(nodeName.substr(t+1).c_str())-1; if(id == markerId) { return id; } } m_ToolManager->GetDataStorage()->Add(rotatedContourNode, workingNode); } } return id; } void mitk::SegTool2D::InteractiveSegmentationBugMessage( const std::string& message ) { MITK_ERROR << "********************************************************************************" << std::endl << " " << message << std::endl << "********************************************************************************" << std::endl << " " << std::endl << " If your image is rotated or the 2D views don't really contain the patient image, try to press the button next to the image selection. " << std::endl << " " << std::endl << " Please file a BUG REPORT: " << std::endl << " http://bugs.mitk.org" << std::endl << " Contain the following information:" << std::endl << " - What image were you working on?" << std::endl << " - Which region of the image?" << std::endl << " - Which tool did you use?" << std::endl << " - What did you do?" << std::endl << " - What happened (not)? What did you expect?" << std::endl; } diff --git a/Modules/Segmentation/Interactions/mitkSegTool2D.h b/Modules/Segmentation/Interactions/mitkSegTool2D.h index 1a21d36089..7a39fff44c 100644 --- a/Modules/Segmentation/Interactions/mitkSegTool2D.h +++ b/Modules/Segmentation/Interactions/mitkSegTool2D.h @@ -1,140 +1,138 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef mitkSegTool2D_h_Included #define mitkSegTool2D_h_Included #include "mitkCommon.h" #include "SegmentationExports.h" #include "mitkTool.h" #include "mitkImage.h" #include "mitkStateEvent.h" #include "mitkPositionEvent.h" #include "mitkPlanePositionManager.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkInteractionConst.h" #include namespace mitk { class BaseRenderer; /** \brief Abstract base class for segmentation tools. \sa Tool \ingroup Interaction \ingroup ToolManagerEtAl Implements 2D segmentation specific helper methods, that might be of use to all kind of 2D segmentation tools. At the moment these are: - Determination of the slice where the user paints upon (DetermineAffectedImageSlice) - Projection of a 3D contour onto a 2D plane/slice SegTool2D tries to structure the interaction a bit. If you pass "PressMoveRelease" as the interaction type of your derived tool, you might implement the methods OnMousePressed, OnMouseMoved, and OnMouseReleased. Yes, your guess about when they are called is correct. \warning Only to be instantiated by mitk::ToolManager. $Author$ */ class Segmentation_EXPORT SegTool2D : public Tool { public: mitkClassMacro(SegTool2D, Tool); /** \brief Calculates for a given Image and PlaneGeometry, which slice of the image (in index corrdinates) is meant by the plane. \return false, if no slice direction seems right (e.g. rotated planes) \param affectedDimension The image dimension, which is constant for all points in the plane, e.g. Transversal --> 2 \param affectedSlice The index of the image slice */ static bool DetermineAffectedImageSlice( const Image* image, const PlaneGeometry* plane, int& affectedDimension, int& affectedSlice ); void SetShowMarkerNodes(bool); - void Enable3DInterpolation(bool); - protected: SegTool2D(); // purposely hidden SegTool2D(const char*); // purposely hidden virtual ~SegTool2D(); virtual bool OnMousePressed (Action*, const StateEvent*); virtual bool OnMouseMoved (Action*, const StateEvent*); virtual bool OnMouseReleased(Action*, const StateEvent*); virtual bool OnInvertLogic (Action*, const StateEvent*); /** \brief Extract the slice of an image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position. */ Image::Pointer GetAffectedImageSliceAs2DImage(const PositionEvent*, const Image* image); /** \brief Extract the slice of the currently selected working image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no working image is selected. */ Image::Pointer GetAffectedWorkingSlice(const PositionEvent*); /** \brief Extract the slice of the currently selected reference image that the user just scribbles on. \return NULL if SegTool2D is either unable to determine which slice was affected, or if there was some problem getting the image data at that position, or just no reference image is selected. */ Image::Pointer GetAffectedReferenceSlice(const PositionEvent*); void WriteBackSegmentationResult (const PositionEvent*, Image*); /** \brief Adds a new node called Contourmarker to the datastorage which holds a mitk::PlanarFigure. By selecting this node the slicestack will be reoriented according to the PlanarFigure's Geometry */ unsigned int AddContourmarker ( const PositionEvent* ); void InteractiveSegmentationBugMessage( const std::string& message ); private: BaseRenderer* m_LastEventSender; unsigned int m_LastEventSlice; //The prefix of the contourmarkername. Suffix is a consecutive number const std::string m_Contourmarkername; bool m_ShowMarkerNodes; bool m_3DInterpolationEnabled; DiffSliceOperation* m_doOperation; DiffSliceOperation* m_undoOperation; }; } // namespace #endif diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index af969d549f..f15f2ad731 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,1239 +1,1221 @@ /*=================================================================== 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 "mitkDataNodeObject.h" #include "mitkProperties.h" #include "mitkSegTool2D.h" #include "mitkGlobalInteraction.h" #include "QmitkStdMultiWidget.h" #include "QmitkNewSegmentationDialog.h" #include #include #include "QmitkSegmentationView.h" #include "QmitkSegmentationPostProcessing.h" #include "QmitkSegmentationOrganNamesHandling.cpp" #include #include //For Segmentation in rotated slices //TODO clean up includes #include "mitkVtkResliceInterpolationProperty.h" #include "mitkPlanarCircle.h" #include "mitkGetModuleContext.h" #include "mitkModule.h" #include "mitkModuleRegistry.h" #include "mitkSegmentationObjectFactory.h" const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; // public methods QmitkSegmentationView::QmitkSegmentationView() :m_Parent(NULL) ,m_Controls(NULL) ,m_MultiWidget(NULL) ,m_RenderingManagerObserverTag(0) { RegisterSegmentationObjectFactory(); } QmitkSegmentationView::~QmitkSegmentationView() { // delete m_PostProcessing; delete m_Controls; } void QmitkSegmentationView::NewNodesGenerated() { // ForceDisplayPreferencesUponAllImages(); } void QmitkSegmentationView::NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType* nodes) { if (!nodes) return; mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); if (!toolManager) return; for (mitk::ToolManager::DataVectorType::iterator iter = nodes->begin(); iter != nodes->end(); ++iter) { this->FireNodeSelected( *iter ); // only last iteration meaningful, multiple generated objects are not taken into account here } } void QmitkSegmentationView::Activated() { // should be moved to ::BecomesVisible() or similar if( m_Controls ) { m_Controls->m_ManualToolSelectionBox->setEnabled( true ); m_Controls->m_OrganToolSelectionBox->setEnabled( true ); m_Controls->m_LesionToolSelectionBox->setEnabled( true ); m_Controls->m_SlicesInterpolator->Enable3DInterpolation( m_Controls->widgetStack->currentWidget() == m_Controls->pageManual ); //TODO Remove Observer itk::ReceptorMemberCommand::Pointer command1 = itk::ReceptorMemberCommand::New(); command1->SetCallbackFunction( this, &QmitkSegmentationView::RenderingManagerReinitialized ); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver( mitk::RenderingManagerViewsInitializedEvent(), command1 ); //Adding observers for node visibility to existing segmentations mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateAnd::Pointer isSegmentation = mitk::NodePredicateAnd::New( isImage, isBinary ); mitk::DataStorage::SetOfObjects::ConstPointer segmentations = this->GetDefaultDataStorage()->GetSubset( isSegmentation ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = segmentations->begin(); iter != segmentations->end(); ++iter) { mitk::DataNode* node = *iter; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( node, node->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); } if(segmentations->Size() > 0) { FireNodeSelected(segmentations->ElementAt(0)); segmentations->ElementAt(0)->GetProperty("visible")->Modified(); } } } void QmitkSegmentationView::Deactivated() { if( m_Controls ) { mitk::RenderingManager::GetInstance()->RemoveObserver( m_RenderingManagerObserverTag ); m_Controls->m_ManualToolSelectionBox->setEnabled( false ); //deactivate all tools m_Controls->m_ManualToolSelectionBox->GetToolManager()->ActivateTool(-1); m_Controls->m_OrganToolSelectionBox->setEnabled( false ); m_Controls->m_LesionToolSelectionBox->setEnabled( false ); m_Controls->m_SlicesInterpolator->EnableInterpolation( false ); //Removing all observers for ( NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter ) { (*dataIter).first->GetProperty("visible")->RemoveObserver( (*dataIter).second ); } m_WorkingDataObserverTags.clear(); if (m_MultiWidget) { mitk::SlicesCoordinator *coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) coordinator->RemoveObserver(m_SlicesRotationObserverTag1); coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) coordinator->RemoveObserver(m_SlicesRotationObserverTag2); } // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); service->RemoveAllPlanePositions(); } } void QmitkSegmentationView::StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ) { SetMultiWidget(&stdMultiWidget); } void QmitkSegmentationView::StdMultiWidgetNotAvailable() { SetMultiWidget(NULL); } void QmitkSegmentationView::StdMultiWidgetClosed( QmitkStdMultiWidget& /*stdMultiWidget*/ ) { SetMultiWidget(NULL); } void QmitkSegmentationView::SetMultiWidget(QmitkStdMultiWidget* multiWidget) { if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag1 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { coordinator->RemoveObserver( m_SlicesRotationObserverTag2 ); } } // save the current multiwidget as the working widget m_MultiWidget = multiWidget; //TODO Remove Observers if (m_MultiWidget) { mitk::SlicesCoordinator* coordinator = m_MultiWidget->GetSlicesRotator(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag1 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } coordinator = m_MultiWidget->GetSlicesSwiveller(); if (coordinator) { itk::ReceptorMemberCommand::Pointer command2 = itk::ReceptorMemberCommand::New(); command2->SetCallbackFunction( this, &QmitkSegmentationView::SliceRotation ); m_SlicesRotationObserverTag2 = coordinator->AddObserver( mitk::SliceRotationEvent(), command2 ); } } //TODO End Remove Observers if (m_Parent) { m_Parent->setEnabled(m_MultiWidget); } // tell the interpolation about toolmanager and multiwidget (and data storage) if (m_Controls && m_MultiWidget) { mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); m_Controls->m_SlicesInterpolator->SetDataStorage( *(this->GetDefaultDataStorage())); m_Controls->m_SlicesInterpolator->Initialize( toolManager, m_MultiWidget ); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences*) { ForceDisplayPreferencesUponAllImages(); } //TODO remove function void QmitkSegmentationView::RenderingManagerReinitialized(const itk::EventObject&) { CheckImageAlignment(); } //TODO remove function void QmitkSegmentationView::SliceRotation(const itk::EventObject&) { CheckImageAlignment(); } // protected slots void QmitkSegmentationView::CreateNewSegmentation() { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>1) { // ask about the name and organ type of the new segmentation QmitkNewSegmentationDialog* dialog = new QmitkNewSegmentationDialog( m_Parent ); // needs a QWidget as parent, "this" is not QWidget QString storedList = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); QStringList organColors; if (storedList.isEmpty()) { organColors = GetDefaultOrganColorString(); } else { /* a couple of examples of how organ names are stored: a simple item is built up like 'name#AABBCC' where #AABBCC is the hexadecimal notation of a color as known from HTML items are stored separated by ';' this makes it necessary to escape occurrences of ';' in name. otherwise the string "hugo;ypsilon#AABBCC;eugen#AABBCC" could not be parsed as two organs but we would get "hugo" and "ypsilon#AABBCC" and "eugen#AABBCC" so the organ name "hugo;ypsilon" is stored as "hugo\;ypsilon" and must be unescaped after loading the following lines could be one split with Perl's negative lookbehind */ // recover string list from BlueBerry view's preferences QString storedString = QString::fromStdString( this->GetPreferences()->GetByteArray("Organ-Color-List","") ); MITK_DEBUG << "storedString: " << storedString.toStdString(); // match a string consisting of any number of repetitions of either "anything but ;" or "\;". This matches everything until the next unescaped ';' QRegExp onePart("(?:[^;]|\\\\;)*"); MITK_DEBUG << "matching " << onePart.pattern().toStdString(); int count = 0; int pos = 0; while( (pos = onePart.indexIn( storedString, pos )) != -1 ) { ++count; int length = onePart.matchedLength(); if (length == 0) break; QString matchedString = storedString.mid(pos, length); MITK_DEBUG << " Captured length " << length << ": " << matchedString.toStdString(); pos += length + 1; // skip separating ';' // unescape possible occurrences of '\;' in the string matchedString.replace("\\;", ";"); // add matched string part to output list organColors << matchedString; } MITK_DEBUG << "Captured " << count << " organ name/colors"; } dialog->SetSuggestionList( organColors ); int dialogReturnValue = dialog->exec(); if ( dialogReturnValue == QDialog::Rejected ) return; // user clicked cancel or pressed Esc or something similar // ask the user about an organ type and name, add this information to the image's (!) propertylist // create a new image of the same dimensions and smallest possible pixel type mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); mitk::Tool* firstTool = toolManager->GetToolById(0); if (firstTool) { try { mitk::DataNode::Pointer emptySegmentation = firstTool->CreateEmptySegmentationNode( image, dialog->GetSegmentationName().toStdString(), dialog->GetColor() ); //Here we change the reslice interpolation mode for a segmentation, so that contours in rotated slice can be shown correctly emptySegmentation->SetProperty( "reslice interpolation", mitk::VtkResliceInterpolationProperty::New(VTK_RESLICE_NEAREST) ); // initialize showVolume to false to prevent recalculating the volume while working on the segmentation emptySegmentation->SetProperty( "showVolume", mitk::BoolProperty::New( false ) ); if (!emptySegmentation) return; // could be aborted by user UpdateOrganList( organColors, dialog->GetSegmentationName(), dialog->GetColor() ); /* escape ';' here (replace by '\;'), see longer comment above */ std::string stringForStorage = organColors.replaceInStrings(";","\\;").join(";").toStdString(); MITK_DEBUG << "Will store: " << stringForStorage; this->GetPreferences()->PutByteArray("Organ-Color-List", stringForStorage ); this->GetPreferences()->Flush(); if(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetSelected(false); } emptySegmentation->SetSelected(true); this->GetDefaultDataStorage()->Add( emptySegmentation, node ); // add as a child, because the segmentation "derives" from the original this->FireNodeSelected( emptySegmentation ); this->OnSelectionChanged( emptySegmentation ); this->SetToolManagerSelection(node, emptySegmentation); } catch (std::bad_alloc) { QMessageBox::warning(NULL,"Create new segmentation","Could not allocate memory for new segmentation"); } } } else { QMessageBox::information(NULL,"Segmentation","Segmentation is currently not supported for 2D images"); } } } else { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a patient image is selected..."; } } void QmitkSegmentationView::OnWorkingNodeVisibilityChanged(/*const itk::Object* caller, const itk::EventObject& e*/) { if (!m_Parent || !m_Parent->isVisible()) return; // The new selection behaviour is: // // When clicking on the checkbox of a segmentation the node will e selected and its reference node either // The previous selected segmentation (if there is one) will be deselected. Additionally a reinit on the // selected segmenation will be performed. // If more than one segmentation is selected the tools will be disabled. if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceDataNew = mitk::DataNode::New(); mitk::DataNode::Pointer workingData; bool workingNodeIsVisible (true); unsigned int numberOfSelectedSegmentations (0); // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); bool isSegmentation(false); node->GetBoolProperty("binary", isSegmentation); if (node->IsSelected() && isSegmentation) { workingNodeIsVisible = node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))); if (!workingNodeIsVisible) return; numberOfSelectedSegmentations++; workingData = node; if (this->GetDefaultDataStorage()->GetSources(node)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(node)->ElementAt(0); } bool isBinary(false); //Find topmost source or first source which is no binary image while (referenceDataNew && this->GetDefaultDataStorage()->GetSources(referenceDataNew)->Size() != 0) { referenceDataNew = this->GetDefaultDataStorage()->GetSources(referenceDataNew)->ElementAt(0); referenceDataNew->GetBoolProperty("binary",isBinary); if (!isBinary) break; } if (workingNodeIsVisible && referenceDataNew) { //Since the binary property of a segmentation can be set to false and afterwards you can create a new segmentation out of it //->could lead to a deadloop NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( referenceDataNew ); if ( searchIter != m_WorkingDataObserverTags.end()) { referenceDataNew->GetProperty("visible")->RemoveObserver( (*searchIter).second ); } referenceDataNew->SetVisibility(true); } //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceDataNew) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); continue; } if (workingData.IsNull() || (workingNodeIsVisible && node != referenceDataNew)) { node->SetVisibility((false)); } } if(numberOfSelectedSegmentations == 1) SetToolManagerSelection(referenceDataNew, workingData); mitk::DataStorage::SetOfObjects::Pointer temp = mitk::DataStorage::SetOfObjects::New(); temp->InsertElement(0,workingData); mitk::TimeSlicedGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(temp); // initialize the views to the bounding geometry /*mitk::RenderingManager::GetInstance()->InitializeViews(bounds); mitk::RenderingManager::GetInstance()->RequestUpdateAll();*/ } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { bool isSeg(false); bool isHelperObject(false); node->GetBoolProperty("helper object", isHelperObject); node->GetBoolProperty("binary", isSeg); if(isSeg && !isHelperObject) { mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations(node, mitk::NodePredicateProperty::New("isContourMarker" , mitk::BoolProperty::New(true))); // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } mitk::DataNode* tempNode = const_cast(node); node->GetProperty("visible")->RemoveObserver( m_WorkingDataObserverTags[tempNode] ); m_WorkingDataObserverTags.erase(tempNode); this->SetToolManagerSelection(NULL, NULL); } } void QmitkSegmentationView::CreateSegmentationFromSurface() { mitk::DataNode::Pointer surfaceNode = m_Controls->MaskSurfaces->GetSelectedNode(); mitk::Surface::Pointer surface(0); if(surfaceNode.IsNotNull()) surface = dynamic_cast ( surfaceNode->GetData() ); if(surface.IsNull()) { this->HandleException( "No surface selected.", m_Parent, true); return; } mitk::DataNode::Pointer imageNode = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::Image::Pointer image(0); if (imageNode.IsNotNull()) image = dynamic_cast( imageNode->GetData() ); if(image.IsNull()) { this->HandleException( "No image selected.", m_Parent, true); return; } mitk::SurfaceToImageFilter::Pointer s2iFilter = mitk::SurfaceToImageFilter::New(); s2iFilter->MakeOutputBinaryOn(); s2iFilter->SetInput(surface); s2iFilter->SetImage(image); s2iFilter->Update(); mitk::DataNode::Pointer resultNode = mitk::DataNode::New(); std::string nameOfResultImage = imageNode->GetName(); nameOfResultImage.append(surfaceNode->GetName()); resultNode->SetProperty("name", mitk::StringProperty::New(nameOfResultImage) ); resultNode->SetProperty("binary", mitk::BoolProperty::New(true) ); resultNode->SetData( s2iFilter->GetOutput() ); this->GetDataStorage()->Add(resultNode, imageNode); } void QmitkSegmentationView::ManualToolSelected(int id) { // disable crosshair movement when a manual drawing tool is active (otherwise too much visual noise) if (m_MultiWidget) { if (id >= 0) { m_MultiWidget->DisableNavigationControllerEventListening(); } else { m_MultiWidget->EnableNavigationControllerEventListening(); } } } void QmitkSegmentationView::ToolboxStackPageChanged(int id) { // interpolation only with manual tools visible m_Controls->m_SlicesInterpolator->EnableInterpolation( id == 0 ); if( id == 0 ) { mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); if( workingData.IsNotNull() ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegImage->show(); m_Controls->lblSegmentation->show(); } } else { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } // this is just a workaround, should be removed when all tools support 3D+t if (id==2) // lesions { mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull()) { if (image->GetDimension()>3) { m_Controls->widgetStack->setCurrentIndex(0); QMessageBox::information(NULL,"Segmentation","Lesion segmentation is currently not supported for 4D images"); } } } } } // protected void QmitkSegmentationView::OnComboBoxSelectionChanged( const mitk::DataNode* node ) { mitk::DataNode* selectedNode = const_cast(node); if( selectedNode != NULL ) { m_Controls->refImageSelector->show(); m_Controls->lblReferenceImageSelectionWarning->hide(); bool isBinary(false); selectedNode->GetBoolProperty("binary", isBinary); if ( isBinary ) { FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); } else if (node != m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) { if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)) m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0)->SetVisibility(false); if (m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)) { m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0)->SetVisibility(false); } FireNodeSelected(selectedNode); selectedNode->SetVisibility(true); SetToolManagerSelection(selectedNode, NULL); } } else { m_Controls->refImageSelector->hide(); m_Controls->lblReferenceImageSelectionWarning->show(); } } void QmitkSegmentationView::OnShowMarkerNodes (bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetTools().size(); for(unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetToolById(i)); if (manualSegmentationTool) { if(state == true) { manualSegmentationTool->SetShowMarkerNodes( true ); } else { manualSegmentationTool->SetShowMarkerNodes( false ); } } } } -void QmitkSegmentationView::On3DInterpolationEnabled (bool state) -{ - mitk::SegTool2D::Pointer manualSegmentationTool; - - unsigned int numberOfExistingTools = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetTools().size(); - - for(unsigned int i = 0; i < numberOfExistingTools; i++) -{ - manualSegmentationTool = dynamic_cast(m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetToolById(i)); - - if (manualSegmentationTool) - { - manualSegmentationTool->Enable3DInterpolation( state ); - } - } -} - void QmitkSegmentationView::OnSelectionChanged(mitk::DataNode* node) { std::vector nodes; nodes.push_back( node ); this->OnSelectionChanged( nodes ); } void QmitkSegmentationView::OnSurfaceSelectionChanged() { // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); } void QmitkSegmentationView::OnSelectionChanged(std::vector nodes) { // if the selected node is a contourmarker if ( !nodes.empty() ) { std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at( 0 )->GetName(); if ( ( numberOfNodes == 1 ) && ( nodeName.find( markerName ) == 0) ) { this->OnContourMarkerSelected( nodes.at( 0 ) ); } } // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull())) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); if (!m_Parent || !m_Parent->isVisible()) return; // reaction to BlueBerry selection events // this method will try to figure out if a relevant segmentation and its corresponding original image were selected // a warning is issued if the selection is invalid // appropriate reactions are triggered otherwise mitk::DataNode::Pointer referenceData = FindFirstRegularImage( nodes ); //m_Controls->refImageSelector->GetSelectedNode(); //FindFirstRegularImage( nodes ); mitk::DataNode::Pointer workingData = FindFirstSegmentation( nodes ); if(referenceData.IsNull() && workingData.IsNull()) return; bool invalidSelection( !nodes.empty() && ( nodes.size() > 2 || // maximum 2 selected nodes (nodes.size() == 2 && (workingData.IsNull() || referenceData.IsNull()) ) || // with two nodes, one must be the original image, one the segmentation ( workingData.GetPointer() == referenceData.GetPointer() ) //one node is selected as reference and working image // one item is always ok (might be working or reference or nothing ) ); if (invalidSelection) { // TODO visible warning when two images are selected MITK_ERROR << "WARNING: No image, too many (>2) or two equal images were selected."; workingData = NULL; if( m_Controls->refImageSelector->GetSelectedNode().IsNull() ) referenceData = NULL; } if ( workingData.IsNotNull() && referenceData.IsNull() ) { // find the DataStorage parent of workingData // try to find a "normal image" parent, select this as reference image mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::NodePredicateProperty::Pointer isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); mitk::NodePredicateNot::Pointer isNotBinary = mitk::NodePredicateNot::New( isBinary ); mitk::NodePredicateAnd::Pointer isNormalImage = mitk::NodePredicateAnd::New( isImage, isNotBinary ); mitk::DataStorage::SetOfObjects::ConstPointer possibleParents = this->GetDefaultDataStorage()->GetSources( workingData, isNormalImage ); if (possibleParents->size() > 0) { if (possibleParents->size() > 1) { // TODO visible warning for this rare case MITK_ERROR << "Selected binary image has multiple parents. Using arbitrary first one for segmentation."; } referenceData = (*possibleParents)[0]; } NodeTagMapType::iterator searchIter = m_WorkingDataObserverTags.find( workingData ); if ( searchIter == m_WorkingDataObserverTags.end() ) { //MITK_INFO<<"Creating new observer"; itk::SimpleMemberCommand::Pointer command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::OnWorkingNodeVisibilityChanged); m_WorkingDataObserverTags.insert( std::pair( workingData, workingData->GetProperty("visible")->AddObserver( itk::ModifiedEvent(), command ) ) ); workingData->GetProperty("visible")->Modified(); return; } if(workingData->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1")))) { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(workingData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(workingData); } else { SetToolManagerSelection(NULL, NULL); FireNodeSelected(workingData); } } else { //set comboBox to reference image disconnect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); m_Controls->refImageSelector->setCurrentIndex( m_Controls->refImageSelector->Find(referenceData) ); connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); // if Image and Surface are selected, enable button if ( (m_Controls->refImageSelector->GetSelectedNode().IsNull()) || (m_Controls->MaskSurfaces->GetSelectedNode().IsNull()) || (!referenceData)) m_Controls->CreateSegmentationFromSurface->setEnabled(false); else m_Controls->CreateSegmentationFromSurface->setEnabled(true); SetToolManagerSelection(referenceData, workingData); FireNodeSelected(referenceData); } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode *node) { //TODO renderWindow anders bestimmen, siehe CheckAlignment QmitkRenderWindow* selectedRenderWindow = 0; QmitkRenderWindow* RenderWindow1 = this->GetActiveStdMultiWidget()->GetRenderWindow1(); QmitkRenderWindow* RenderWindow2 = this->GetActiveStdMultiWidget()->GetRenderWindow2(); QmitkRenderWindow* RenderWindow3 = this->GetActiveStdMultiWidget()->GetRenderWindow3(); QmitkRenderWindow* RenderWindow4 = this->GetActiveStdMultiWidget()->GetRenderWindow4(); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow1->GetRenderer())) { selectedRenderWindow = RenderWindow1; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow2->GetRenderer())) { selectedRenderWindow = RenderWindow2; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow3->GetRenderer())) { selectedRenderWindow = RenderWindow3; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, RenderWindow4->GetRenderer())) { selectedRenderWindow = RenderWindow4; } // make node visible if (selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t+1).c_str())-1; // gets the context of the "Mitk" (Core) module (always has id 1) // TODO Workaround until CTL plugincontext is available mitk::ModuleContext* context = mitk::ModuleRegistry::GetModule(1)->GetModuleContext(); // Workaround end mitk::ServiceReference serviceRef = context->GetServiceReference(); //mitk::ServiceReference serviceRef = mitk::GetModuleContext()->GetServiceReference(); mitk::PlanePositionManagerService* service = dynamic_cast(context->GetService(serviceRef)); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); selectedRenderWindow->GetRenderer()->GetDisplayGeometry()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstRegularImage( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { //mitk::DataNode::Pointer node = i.value() bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } // make sure this is not a binary image bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper mitk::Image if (isImage && !isSegmentation) return nodes.at(i); } return NULL; } mitk::DataNode::Pointer QmitkSegmentationView::FindFirstSegmentation( std::vector nodes ) { if (nodes.empty()) return NULL; for(unsigned int i = 0; i < nodes.size(); ++i) { bool isImage(false); if (nodes.at(i)->GetData()) { isImage = dynamic_cast(nodes.at(i)->GetData()) != NULL; } bool isSegmentation(false); nodes.at(i)->GetBoolProperty("binary", isSegmentation); // return first proper binary mitk::Image if (isImage && isSegmentation) { return nodes.at(i); } } return NULL; } void QmitkSegmentationView::SetToolManagerSelection(const mitk::DataNode* referenceData, const mitk::DataNode* workingData) { // called as a result of new BlueBerry selections // tells the ToolManager for manual segmentation about new selections // updates GUI information about what the user should select mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetReferenceData(const_cast(referenceData)); toolManager->SetWorkingData( const_cast(workingData)); // check original image m_Controls->btnNewSegmentation->setEnabled(referenceData != NULL); if (referenceData) { m_Controls->lblReferenceImageSelectionWarning->hide(); } else { m_Controls->lblReferenceImageSelectionWarning->show(); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } //TODO remove statement // check, wheter reference image is aligned like render windows. Otherwise display a visible warning (because 2D tools will probably not work) CheckImageAlignment(); // check segmentation if (referenceData) { if (!workingData) { m_Controls->lblWorkingImageSelectionWarning->show(); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); } } else { m_Controls->lblWorkingImageSelectionWarning->hide(); this->FireNodeSelected(const_cast(workingData)); if( m_Controls->widgetStack->currentIndex() == 0 ) { m_Controls->lblSegmentation->setText( workingData->GetName().c_str() ); m_Controls->lblSegmentation->show(); m_Controls->lblSegImage->show(); } } } } //TODO remove function void QmitkSegmentationView::CheckImageAlignment() { bool wrongAlignment(true); mitk::DataNode::Pointer node = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); if (node.IsNotNull()) { mitk::Image::Pointer image = dynamic_cast( node->GetData() ); if (image.IsNotNull() && m_MultiWidget) { wrongAlignment = !( IsRenderWindowAligned(m_MultiWidget->GetRenderWindow1(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow2(), image ) && IsRenderWindowAligned(m_MultiWidget->GetRenderWindow3(), image ) ); } } m_Controls->lblAlignmentWarning->setVisible(wrongAlignment); } //TODO remove function bool QmitkSegmentationView::IsRenderWindowAligned(QmitkRenderWindow* renderWindow, mitk::Image* image) { if (!renderWindow) return false; // for all 2D renderwindows of m_MultiWidget check alignment mitk::PlaneGeometry::ConstPointer displayPlane = dynamic_cast( renderWindow->GetRenderer()->GetCurrentWorldGeometry2D() ); if (displayPlane.IsNull()) return false; int affectedDimension(-1); int affectedSlice(-1); return mitk::SegTool2D::DetermineAffectedImageSlice( image, displayPlane, affectedDimension, affectedSlice ); } //TODO remove function void QmitkSegmentationView::ForceDisplayPreferencesUponAllImages() { if (!m_Parent || !m_Parent->isVisible()) return; // check all images and segmentations in DataStorage: // (items in brackets are implicitly done by previous steps) // 1. // if a reference image is selected, // show the reference image // and hide all other images (orignal and segmentation), // (and hide all segmentations of the other original images) // and show all the reference's segmentations // if no reference image is selected, do do nothing // // 2. // if a segmentation is selected, // show it // (and hide all all its siblings (childs of the same parent, incl, NULL parent)) // if no segmentation is selected, do nothing if (!m_Controls) return; // might happen on initialization (preferences loaded) mitk::DataNode::Pointer referenceData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetReferenceData(0); mitk::DataNode::Pointer workingData = m_Controls->m_ManualToolSelectionBox->GetToolManager()->GetWorkingData(0); // 1. if (referenceData.IsNotNull()) { // iterate all images mitk::TNodePredicateDataType::Pointer isImage = mitk::TNodePredicateDataType::New(); mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDefaultDataStorage()->GetSubset( isImage ); //mitk::DataStorage::SetOfObjects::ConstPointer allSegmentationChilds = this->GetDefaultDataStorage()->GetDerivations(referenceData, isImage ); for ( mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { mitk::DataNode* node = *iter; // apply display preferences ApplyDisplayOptions(node); // set visibility if(!node->IsSelected() || (node->IsSelected() && !node->IsVisible(mitk::BaseRenderer::GetInstance( mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))))) node->SetVisibility((node == referenceData) || node->IsSelected() ); } } // 2. //if (workingData.IsNotNull() && !workingData->IsSelected()) //{ // workingData->SetVisibility(true); //} mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (!node) return; bool isBinary(false); node->GetPropertyValue("binary", isBinary); if (isBinary) { node->SetProperty( "outline binary", mitk::BoolProperty::New( this->GetPreferences()->GetBool("draw outline", true)) ); node->SetProperty( "outline width", mitk::FloatProperty::New( 2.0 ) ); node->SetProperty( "opacity", mitk::FloatProperty::New( this->GetPreferences()->GetBool("draw outline", true) ? 1.0 : 0.3 ) ); node->SetProperty( "volumerendering", mitk::BoolProperty::New( this->GetPreferences()->GetBool("volume rendering", false) ) ); } } void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { // setup the basic GUI of this view m_Parent = parent; m_Controls = new Ui::QmitkSegmentationControls; m_Controls->setupUi(parent); m_Controls->lblWorkingImageSelectionWarning->hide(); m_Controls->lblAlignmentWarning->hide(); m_Controls->lblSegImage->hide(); m_Controls->lblSegmentation->hide(); m_Controls->refImageSelector->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->refImageSelector->SetPredicate(mitk::NodePredicateDataType::New("Image")); if( m_Controls->refImageSelector->GetSelectedNode().IsNotNull() ) m_Controls->lblReferenceImageSelectionWarning->hide(); else m_Controls->refImageSelector->hide(); mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); toolManager->SetDataStorage( *(this->GetDefaultDataStorage()) ); assert ( toolManager ); // all part of open source MITK m_Controls->m_ManualToolSelectionBox->SetGenerateAccelerators(true); m_Controls->m_ManualToolSelectionBox->SetToolGUIArea( m_Controls->m_ManualToolGUIContainer ); m_Controls->m_ManualToolSelectionBox->SetDisplayedToolGroups("Add Subtract Paint Wipe 'Region Growing' Correction Fill Erase"); m_Controls->m_ManualToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingData ); // available only in the 3M application if ( !m_Controls->m_OrganToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 1, false ); } m_Controls->m_OrganToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_OrganToolSelectionBox->SetToolGUIArea( m_Controls->m_OrganToolGUIContainer ); m_Controls->m_OrganToolSelectionBox->SetDisplayedToolGroups("'Hippocampus left' 'Hippocampus right' 'Lung left' 'Lung right' 'Liver' 'Heart LV' 'Endocard LV' 'Epicard LV' 'Prostate'"); m_Controls->m_OrganToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); // available only in the 3M application if ( !m_Controls->m_LesionToolSelectionBox->children().count() ) { m_Controls->widgetStack->setItemEnabled( 2, false ); } m_Controls->m_LesionToolSelectionBox->SetToolManager( *toolManager ); m_Controls->m_LesionToolSelectionBox->SetToolGUIArea( m_Controls->m_LesionToolGUIContainer ); m_Controls->m_LesionToolSelectionBox->SetDisplayedToolGroups("'Lymph Node'"); m_Controls->m_LesionToolSelectionBox->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceData ); toolManager->NewNodesGenerated += mitk::MessageDelegate( this, &QmitkSegmentationView::NewNodesGenerated ); // update the list of segmentations toolManager->NewNodeObjectsGenerated += mitk::MessageDelegate1( this, &QmitkSegmentationView::NewNodeObjectsGenerated ); // update the list of segmentations // create signal/slot connections connect( m_Controls->refImageSelector, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnComboBoxSelectionChanged( const mitk::DataNode* ) ) ); connect( m_Controls->btnNewSegmentation, SIGNAL(clicked()), this, SLOT(CreateNewSegmentation()) ); connect( m_Controls->CreateSegmentationFromSurface, SIGNAL(clicked()), this, SLOT(CreateSegmentationFromSurface()) ); connect( m_Controls->m_ManualToolSelectionBox, SIGNAL(ToolSelected(int)), this, SLOT(ManualToolSelected(int)) ); connect( m_Controls->widgetStack, SIGNAL(currentChanged(int)), this, SLOT(ToolboxStackPageChanged(int)) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->MaskSurfaces, SIGNAL( OnSelectionChanged( const mitk::DataNode* ) ), this, SLOT( OnSurfaceSelectionChanged( ) ) ); connect(m_Controls->m_SlicesInterpolator, SIGNAL(SignalShowMarkerNodes(bool)), this, SLOT(OnShowMarkerNodes(bool))); - connect(m_Controls->m_SlicesInterpolator, SIGNAL(Signal3DInterpolationEnabled(bool)), this, SLOT(On3DInterpolationEnabled(bool))); m_Controls->MaskSurfaces->SetDataStorage(this->GetDefaultDataStorage()); m_Controls->MaskSurfaces->SetPredicate(mitk::NodePredicateDataType::New("Surface")); //// create helper class to provide context menus for segmentations in data manager // m_PostProcessing = new QmitkSegmentationPostProcessing(this->GetDefaultDataStorage(), this, m_Parent); } //void QmitkSegmentationView::OnPlaneModeChanged(int i) //{ // //if plane mode changes, disable all tools // if (m_MultiWidget) // { // mitk::ToolManager* toolManager = m_Controls->m_ManualToolSelectionBox->GetToolManager(); // // if (toolManager) // { // if (toolManager->GetActiveToolID() >= 0) // { // toolManager->ActivateTool(-1); // } // else // { // m_MultiWidget->EnableNavigationControllerEventListening(); // } // } // } //} // ATTENTION some methods for handling the known list of (organ names, colors) are defined in QmitkSegmentationOrganNamesHandling.cpp diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h index ee3296d2ac..86802542e1 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h @@ -1,168 +1,166 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ #ifndef QmitkSegmentationView_h #define QmitkSegmentationView_h #include "QmitkFunctionality.h" #include #include "ui_QmitkSegmentationControls.h" class QmitkRenderWindow; // class QmitkSegmentationPostProcessing; /** * \ingroup ToolManagerEtAl * \ingroup org_mitk_gui_qt_segmentation_internal * \warning Implementation of this class is split up into two .cpp files to make things more compact. Check both this file and QmitkSegmentationOrganNamesHandling.cpp */ class QmitkSegmentationView : public QmitkFunctionality { Q_OBJECT public: QmitkSegmentationView(); virtual ~QmitkSegmentationView(); typedef std::map NodeTagMapType; /*! \brief Invoked when the DataManager selection changed */ virtual void OnSelectionChanged(mitk::DataNode* node); virtual void OnSelectionChanged(std::vector nodes); // reaction to new segmentations being created by segmentation tools void NewNodesGenerated(); void NewNodeObjectsGenerated(mitk::ToolManager::DataVectorType*); // QmitkFunctionality's activate/deactivate virtual void Activated(); virtual void Deactivated(); // QmitkFunctionality's changes regarding THE QmitkStdMultiWidget virtual void StdMultiWidgetAvailable(QmitkStdMultiWidget& stdMultiWidget); virtual void StdMultiWidgetNotAvailable(); virtual void StdMultiWidgetClosed(QmitkStdMultiWidget& stdMultiWidget); // BlueBerry's notification about preference changes (e.g. from a dialog) virtual void OnPreferencesChanged(const berry::IBerryPreferences*); // observer to mitk::RenderingManager's RenderingManagerViewsInitializedEvent event void RenderingManagerReinitialized(const itk::EventObject&); // observer to mitk::SliceController's SliceRotation event void SliceRotation(const itk::EventObject&); static const std::string VIEW_ID; protected slots: void OnComboBoxSelectionChanged(const mitk::DataNode* node); // reaction to the button "New segmentation" void CreateNewSegmentation(); // reaction to the button "New segmentation" void CreateSegmentationFromSurface(); // called when a segmentation tool is activated void ManualToolSelected(int id); // called when one of "Manual", "Organ", "Lesion" pages of the QToolbox is selected void ToolboxStackPageChanged(int id); void OnSurfaceSelectionChanged(); //called when the checkbox Remember Contour Positions is selected/deselected void OnWorkingNodeVisibilityChanged(); void OnShowMarkerNodes(bool); - void On3DInterpolationEnabled(bool); - protected: // a type for handling lists of DataNodes typedef std::vector NodeList; // set available multiwidget void SetMultiWidget(QmitkStdMultiWidget* multiWidget); // actively query the current selection of data manager //void PullCurrentDataManagerSelection(); // reactions to selection events from data manager (and potential other senders) //void BlueBerrySelectionChanged(berry::IWorkbenchPart::Pointer sourcepart, berry::ISelection::ConstPointer selection); mitk::DataNode::Pointer FindFirstRegularImage( std::vector nodes ); mitk::DataNode::Pointer FindFirstSegmentation( std::vector nodes ); // propagate BlueBerry selection to ToolManager for manual segmentation void SetToolManagerSelection(const mitk::DataNode* referenceData, const mitk::DataNode* workingData); // checks if selected reference image is aligned with the slices stack orientation of the StdMultiWidget void CheckImageAlignment(); // checks if given render window aligns with the slices of given image bool IsRenderWindowAligned(QmitkRenderWindow* renderWindow, mitk::Image* image); // make sure all images/segmentations look as selected by the users in this view's preferences void ForceDisplayPreferencesUponAllImages(); // decorates a DataNode according to the user preference settings void ApplyDisplayOptions(mitk::DataNode* node); // GUI setup void CreateQtPartControl(QWidget* parent); // handling of a list of known (organ name, organ color) combination // ATTENTION these methods are defined in QmitkSegmentationOrganNamesHandling.cpp QStringList GetDefaultOrganColorString(); void UpdateOrganList(QStringList& organColors, const QString& organname, mitk::Color colorname); void AppendToOrganList(QStringList& organColors, const QString& organname, int r, int g, int b); // If a contourmarker is selected, the plane in the related widget will be reoriented according to the marker`s geometry void OnContourMarkerSelected (const mitk::DataNode* node); void NodeRemoved(const mitk::DataNode* node); // the Qt parent of our GUI (NOT of this object) QWidget* m_Parent; // our GUI Ui::QmitkSegmentationControls * m_Controls; // THE currently existing QmitkStdMultiWidget QmitkStdMultiWidget * m_MultiWidget; // QmitkSegmentationPostProcessing* m_PostProcessing; unsigned long m_RenderingManagerObserverTag; unsigned long m_SlicesRotationObserverTag1; unsigned long m_SlicesRotationObserverTag2; unsigned long m_VisibilityChangedObserverTag; NodeTagMapType m_WorkingDataObserverTags; }; #endif /*QMITKsegmentationVIEW_H_*/