diff --git a/CMakeLists.txt b/CMakeLists.txt index e23b0e7c..6da4f6d9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -55,12 +55,12 @@ endforeach() # These four fields should be modified when versions change SET(SNAP_VERSION_MAJOR 3) SET(SNAP_VERSION_MINOR 4) -SET(SNAP_VERSION_PATCH 1) -SET(SNAP_VERSION_QUALIFIER "-alpha") +SET(SNAP_VERSION_PATCH 0) +SET(SNAP_VERSION_QUALIFIER "") # These fields should also be modified each time -SET(SNAP_VERSION_RELEASE_DATE "20151017") -SET(SNAP_VERSION_RELEASE_DATE_FORMATTED "Oct 17, 2015") +SET(SNAP_VERSION_RELEASE_DATE "20151130") +SET(SNAP_VERSION_RELEASE_DATE_FORMATTED "Nov 30, 2015") # This field should only change when the format of the settings files changes # in a non backwards-compatible way @@ -209,6 +209,7 @@ SET(LOGIC_CXX Logic/Framework/IRISImageData.cxx Logic/Framework/LayerIterator.cxx Logic/Framework/SNAPImageData.cxx + Logic/Framework/JOINImageData.cxx Logic/Framework/UndoDataManager_LabelType.cxx Logic/ImageWrapper/CommonRepresentationPolicy.cxx Logic/ImageWrapper/DisplayMappingPolicy.cxx @@ -308,6 +309,7 @@ SET(LOGIC_HEADERS Logic/Framework/LayerAssociation.txx Logic/Framework/LayerIterator.h Logic/Framework/SNAPImageData.h + Logic/Framework/JOINImageData.h Logic/Framework/UndoDataManager.h Logic/Framework/UndoDataManager.txx Logic/ImageWrapper/CommonRepresentationPolicy.h @@ -409,6 +411,8 @@ SET(UI_GENERIC_CXX GUI/Model/SnakeROIModel.cxx GUI/Model/SnakeROIResampleModel.cxx GUI/Model/SnakeWizardModel.cxx + GUI/Model/JoinModel.cxx + GUI/Model/GlobalWSWizardModel.cxx GUI/Model/StateManagement.cxx GUI/Model/SynchronizationModel.cxx GUI/Model/UIAction.cxx @@ -481,6 +485,8 @@ SET(UI_GENERIC_HEADERS GUI/Model/SnakeROIModel.h GUI/Model/SnakeROIResampleModel.h GUI/Model/SnakeWizardModel.h + GUI/Model/JoinModel.h + GUI/Model/GlobalWSWizardModel.h GUI/Model/StateManagement.h GUI/Model/SynchronizationModel.h GUI/Model/UIAction.h @@ -552,6 +558,8 @@ SET(UI_QT_CXX GUI/Qt/Components/RecentHistoryItemsView.cxx GUI/Qt/Components/SnakeToolROIPanel.cxx GUI/Qt/Components/SnakeWizardPanel.cxx + GUI/Qt/Components/JoinDataPanel.cxx + GUI/Qt/Components/GlobalWSWizardPanel.cxx GUI/Qt/Components/SNAPComponent.cxx GUI/Qt/Components/SNAPQtCommon.cxx GUI/Qt/Components/SliceViewPanel.cxx @@ -576,6 +584,7 @@ SET(UI_QT_CXX GUI/Qt/View/PolygonDrawingInteractionMode.cxx GUI/Qt/View/SliceWindowInteractionDelegateWidget.cxx GUI/Qt/View/SnakeROIInteractionMode.cxx + GUI/Qt/View/JoinInteractionMode.cxx GUI/Qt/View/ThumbnailInteractionMode.cxx GUI/Qt/Windows/AboutDialog.cxx GUI/Qt/Windows/DropActionDialog.cxx @@ -636,6 +645,8 @@ SET(UI_MOC_HEADERS GUI/Qt/Components/RecentHistoryItemsView.h GUI/Qt/Components/SnakeToolROIPanel.h GUI/Qt/Components/SnakeWizardPanel.h + GUI/Qt/Components/JoinDataPanel.h + GUI/Qt/Components/GlobalWSWizardPanel.h GUI/Qt/Components/SNAPComponent.h GUI/Qt/Components/SliceViewPanel.h GUI/Qt/Components/SynchronizationInspector.h @@ -659,6 +670,7 @@ SET(UI_MOC_HEADERS GUI/Qt/View/PolygonDrawingInteractionMode.h GUI/Qt/View/SliceWindowInteractionDelegateWidget.h GUI/Qt/View/SnakeROIInteractionMode.h + GUI/Qt/View/JoinInteractionMode.h GUI/Qt/View/ThumbnailInteractionMode.h GUI/Qt/Windows/AboutDialog.h GUI/Qt/Windows/DropActionDialog.h @@ -734,6 +746,8 @@ SET(UI_FORMS GUI/Qt/Components/SliceViewPanel.ui GUI/Qt/Components/SnakeToolROIPanel.ui GUI/Qt/Components/SnakeWizardPanel.ui + GUI/Qt/Components/JoinDataPanel.ui + GUI/Qt/Components/GlobalWSWizardPanel.ui GUI/Qt/Components/SynchronizationInspector.ui GUI/Qt/Components/ViewPanel3D.ui GUI/Qt/Components/ZoomInspector.ui diff --git a/CTestConfig.cmake b/CTestConfig.cmake index 1915c652..2a7898b8 100644 --- a/CTestConfig.cmake +++ b/CTestConfig.cmake @@ -6,10 +6,10 @@ ## ENABLE_TESTING() ## INCLUDE(CTest) -set(CTEST_PROJECT_NAME "ITK-SNAP master") -set(CTEST_NIGHTLY_START_TIME "01:00:00 UTC") +set(CTEST_PROJECT_NAME "ITK-SNAP 3.4") +set(CTEST_NIGHTLY_START_TIME "00:00:00 UTC") set(CTEST_DROP_METHOD "http") set(CTEST_DROP_SITE "itksnap.org") -set(CTEST_DROP_LOCATION "/cdash/submit.php?project=ITK-SNAP+master") +set(CTEST_DROP_LOCATION "/cdash/submit.php?project=ITK-SNAP+3.4") set(CTEST_DROP_SITE_CDASH TRUE) diff --git a/Common/SNAPCommon.h b/Common/SNAPCommon.h index 216684de..79e0bfdd 100644 --- a/Common/SNAPCommon.h +++ b/Common/SNAPCommon.h @@ -92,6 +92,9 @@ extern const char SNAPBuildInfo[]; // UnaryFunctorCache in the GreyImageWrapper type. Greyscale instensities // 0 to MAXGREYVAL are used in a cache table, which would be too big with int typedef unsigned short LabelType; +typedef itk::IdentifierType GWSType; +typedef GWSType JSRType; +typedef float WSRType; typedef short GreyType; extern const GreyType MAXGREYVAL; extern const GreyType MINGREYVAL; @@ -117,6 +120,7 @@ enum LayerRole SNAP_ROLE = 0x0004, LABEL_ROLE = 0x0008, NO_ROLE = 0x0010, + JOIN_ROLE = 0x0020, ALL_ROLES = 0xffffffff }; diff --git a/GUI/Model/CursorInspectionModel.cxx b/GUI/Model/CursorInspectionModel.cxx index c0ad6520..f2612512 100644 --- a/GUI/Model/CursorInspectionModel.cxx +++ b/GUI/Model/CursorInspectionModel.cxx @@ -108,6 +108,7 @@ void CursorInspectionModel::SetParentModel(GlobalUIModel *parent) int role = MAIN_ROLE | OVERLAY_ROLE | + JOIN_ROLE | SNAP_ROLE; CurrentVoxelInfoItemSetDomain dom(parent, role); diff --git a/GUI/Model/GlobalUIModel.cxx b/GUI/Model/GlobalUIModel.cxx index f2690d8f..c98b91fe 100644 --- a/GUI/Model/GlobalUIModel.cxx +++ b/GUI/Model/GlobalUIModel.cxx @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -47,6 +48,7 @@ #include #include #include +#include #include #include #include @@ -112,6 +114,9 @@ GlobalUIModel::GlobalUIModel() m_SnakeROIModel[i] = SnakeROIModel::New(); m_SnakeROIModel[i]->SetParent(m_SliceModel[i]); + m_JoinModel[i] = JoinModel::New(); + m_JoinModel[i]->SetParent(m_SliceModel[i]); + m_PaintbrushModel[i] = PaintbrushModel::New(); m_PaintbrushModel[i]->SetParent(m_SliceModel[i]); @@ -149,7 +154,7 @@ GlobalUIModel::GlobalUIModel() m_LoadedLayersSelectionModel->SetParentModel(this); m_LoadedLayersSelectionModel->SetRoleFilter( MAIN_ROLE | OVERLAY_ROLE | - SNAP_ROLE); + SNAP_ROLE | JOIN_ROLE); // 3D model m_Model3D = Generic3DModel::New(); @@ -175,6 +180,10 @@ GlobalUIModel::GlobalUIModel() m_SnakeROIResampleModel = SnakeROIResampleModel::New(); m_SnakeROIResampleModel->SetParentModel(this); + // GlobalWS model + m_GlobalWSWizardModel = GlobalWSWizardModel::New(); + m_GlobalWSWizardModel->SetParentModel(this); + // Synchronization model m_SynchronizationModel = SynchronizationModel::New(); m_SynchronizationModel->SetParentModel(this); @@ -311,11 +320,12 @@ bool GlobalUIModel::CheckState(UIState state) case UIF_BASEIMG_LOADED: return m_Driver->IsMainImageLoaded(); case UIF_IRIS_WITH_BASEIMG_LOADED: - return m_Driver->IsMainImageLoaded() && !m_Driver->IsSnakeModeActive(); + return m_Driver->IsMainImageLoaded() && !m_Driver->IsSnakeModeActive();// && !m_Driver->IsJoinModeActive(); ///adding this will disable lablel change in Join mode, use UIF_NOT_SNAKE_OR_JOIN_MODE if needed. case UIF_IRIS_MODE: - return !m_Driver->IsSnakeModeActive(); + return !m_Driver->IsSnakeModeActive() && !m_Driver->IsJoinModeActive();//not sure if Join extension here has any effect case UIF_IRIS_WITH_OVERLAY_LOADED: return m_Driver->IsMainImageLoaded() && !m_Driver->IsSnakeModeActive() + && !m_Driver->IsJoinModeActive() && m_Driver->GetCurrentImageData()->GetNumberOfOverlays() > 0; case UIF_ROI_VALID: break; @@ -333,6 +343,10 @@ bool GlobalUIModel::CheckState(UIState state) return m_Driver->GetCurrentImageData()->IsOverlayLoaded(); case UIF_SNAKE_MODE: return m_Driver->IsSnakeModeActive(); + case UIF_JOIN_MODE: + return m_Driver->IsJoinModeActive(); + case UIF_NOT_SNAKE_OR_JOIN_MODE: + return m_Driver->IsMainImageLoaded() && !m_Driver->IsSnakeModeActive() && !m_Driver->IsJoinModeActive(); case UIF_LEVEL_SET_ACTIVE: return m_Driver->IsSnakeModeLevelSetActive(); case UIF_MULTIPLE_BASE_LAYERS: @@ -402,6 +416,99 @@ void GlobalUIModel::ToggleOverlayVisibility() m_LayerGeneralPropertiesModel->SetLayer(curr_layer); } +void GlobalUIModel::ToggleJsrcVisibility() +{ + // Are we in JOIN mode? + if(CheckState(UIF_JOIN_MODE)){ + GenericImageData *id = m_Driver->GetCurrentImageData(); + + // Remember what layer is current in the general properties model + ImageWrapperBase *curr_layer = m_LayerGeneralPropertiesModel->GetLayer(); + + // Apply the toggle for all overlays + for(LayerIterator it = id->GetLayers(JOIN_ROLE); !it.IsAtEnd(); ++it){ + //if(dynamic_cast(it.GetLayer())){//not tested + if(strcmp(it.GetLayer()->GetNickname().c_str(), "Join Source Image") == 0){ + m_LayerGeneralPropertiesModel->SetLayer(it.GetLayer()); + m_LayerGeneralPropertiesModel->GetLayerVisibilityModel()->SetValue( + !m_LayerGeneralPropertiesModel->GetLayerVisibilityModel()->GetValue()); + } + } + + // Restore the active layer + m_LayerGeneralPropertiesModel->SetLayer(curr_layer); + } +} + +void GlobalUIModel::SetJsrcVisibility(bool set) +{ + // Are we in JOIN mode? + if(CheckState(UIF_JOIN_MODE)){ + GenericImageData *id = m_Driver->GetCurrentImageData(); + + // Remember what layer is current in the general properties model + ImageWrapperBase *curr_layer = m_LayerGeneralPropertiesModel->GetLayer(); + + // Apply the toggle for all overlays + for(LayerIterator it = id->GetLayers(JOIN_ROLE); !it.IsAtEnd(); ++it){ + //if(dynamic_cast(it.GetLayer())){//not tested + if(strcmp(it.GetLayer()->GetNickname().c_str(), "Join Source Image") == 0){ + m_LayerGeneralPropertiesModel->SetLayer(it.GetLayer()); + m_LayerGeneralPropertiesModel->GetLayerVisibilityModel()->SetValue(set); + } + } + + // Restore the active layer + m_LayerGeneralPropertiesModel->SetLayer(curr_layer); + } +} + +void GlobalUIModel::SetJdstVisibility(bool set) +{ + // Are we in JOIN mode? + if(CheckState(UIF_JOIN_MODE)){ + GenericImageData *id = m_Driver->GetCurrentImageData(); + + // Remember what layer is current in the general properties model + ImageWrapperBase *curr_layer = m_LayerGeneralPropertiesModel->GetLayer(); + + // Apply the toggle for all overlays + for(LayerIterator it = id->GetLayers(JOIN_ROLE); !it.IsAtEnd(); ++it){ + //if(dynamic_cast(it.GetLayer())){//not tested + if(strcmp(it.GetLayer()->GetNickname().c_str(), "Join Destination Image") == 0){ + m_LayerGeneralPropertiesModel->SetLayer(it.GetLayer()); + m_LayerGeneralPropertiesModel->GetLayerVisibilityModel()->SetValue(set); + } + } + + // Restore the active layer + m_LayerGeneralPropertiesModel->SetLayer(curr_layer); + } +} + +void GlobalUIModel::SetWsrcVisibility(bool set) +{ + // Are we in JOIN mode? + if(CheckState(UIF_JOIN_MODE)){ + GenericImageData *id = m_Driver->GetCurrentImageData(); + + // Remember what layer is current in the general properties model + ImageWrapperBase *curr_layer = m_LayerGeneralPropertiesModel->GetLayer(); + + // Apply the toggle for all overlays + for(LayerIterator it = id->GetLayers(JOIN_ROLE); !it.IsAtEnd(); ++it){ + //if(dynamic_cast(it.GetLayer())){//not tested + if(strcmp(it.GetLayer()->GetNickname().c_str(), "GWS Source Image") == 0){ + m_LayerGeneralPropertiesModel->SetLayer(it.GetLayer()); + m_LayerGeneralPropertiesModel->GetLayerVisibilityModel()->SetValue(set); + } + } + + // Restore the active layer + m_LayerGeneralPropertiesModel->SetLayer(curr_layer); + } +} + void GlobalUIModel::AdjustOverlayOpacity(int delta) { // Are we in tiled mode or in stack mode? @@ -789,6 +896,38 @@ GlobalUIModel::CreateIOWizardModelForSave(ImageWrapperBase *layer, LayerRole rol SmartPtr delegate = m_Driver->CreateSaveDelegateForLayer(layer, role); + // Figure out the category name + std::string category; + switch(role) + { + case MAIN_ROLE: + category = "Main Image"; + break; + case OVERLAY_ROLE: + category = "Overlay Image"; + break; + case SNAP_ROLE: + if(dynamic_cast(layer)) + category = "Speed Image"; + else if(dynamic_cast(layer)) + category = "Level Set Image"; + break; + case JOIN_ROLE: + if(dynamic_cast(layer)) + category = "Join Source Image"; + else if(dynamic_cast(layer)) + category = "Join Destination Image"; + else if(dynamic_cast(layer)) + category = "GWS Source Image"; + break; + case LABEL_ROLE: + category = "Segmentation Image"; + break; + case NO_ROLE: + case ALL_ROLES: + break; + } + // Create a model for IO SmartPtr modelIO = ImageIOWizardModel::New(); modelIO->InitializeForSave(this, delegate, delegate->GetCategory().c_str()); diff --git a/GUI/Model/GlobalUIModel.h b/GUI/Model/GlobalUIModel.h index e33af84a..15f7a9e2 100644 --- a/GUI/Model/GlobalUIModel.h +++ b/GUI/Model/GlobalUIModel.h @@ -57,6 +57,8 @@ class CursorInspectionModel; class SnakeROIModel; class SnakeWizardModel; class SnakeROIResampleModel; +class JoinModel; +class GlobalWSWizardModel; class ProgressReporterDelegate; class ReorientImageModel; class DisplayLayoutModel; @@ -157,12 +159,18 @@ class GlobalUIModel : public AbstractModel return m_PolygonDrawingModel[i]; } - /** Get the polygon drawing model for each slice */ + /** Get the snake drawing model for each slice */ SnakeROIModel *GetSnakeROIModel(unsigned int i) const { return m_SnakeROIModel[i]; } + /** Get the join drawing model for each slice */ + JoinModel *GetJoinModel(unsigned int i) const + { + return m_JoinModel[i]; + } + PaintbrushModel *GetPaintbrushModel(unsigned int i) const { return m_PaintbrushModel[i]; @@ -204,6 +212,9 @@ class GlobalUIModel : public AbstractModel /** The model that handles snake wizard interaction */ irisGetMacro(SnakeWizardModel, SnakeWizardModel *) + /** The model that handles GlobalWS wizard interaction */ + irisGetMacro(GlobalWSWizardModel, GlobalWSWizardModel *) + /** The model handling display layout properties */ irisGetMacro(DisplayLayoutModel, DisplayLayoutModel *) @@ -257,6 +268,18 @@ class GlobalUIModel : public AbstractModel /** Method to toggle overlay visibility (all or selected overlays) */ void ToggleOverlayVisibility(); + /** Method to toggle Jsrc visibility */ + void ToggleJsrcVisibility(); + + /** Method to set Jsrc visibility */ + void SetJsrcVisibility(bool set); + + /** Method to set Jdst visibility */ + void SetJdstVisibility(bool set); + + /** Method to set Wsrc visibility */ + void SetWsrcVisibility(bool set); + /** Method to adjust overlay opacity (all or selected overlays) */ void AdjustOverlayOpacity(int delta); @@ -337,6 +360,9 @@ class GlobalUIModel : public AbstractModel // Models for snake ROI drawing SmartPtr m_SnakeROIModel[3]; + // Models for Click'n'Join + SmartPtr m_JoinModel[3]; + // Models for paintbrush drawing SmartPtr m_PaintbrushModel[3]; @@ -376,6 +402,9 @@ class GlobalUIModel : public AbstractModel // The snake wizard model SmartPtr m_SnakeWizardModel; + // The GlobalWS wizard model + SmartPtr m_GlobalWSWizardModel; + // Display layout model SmartPtr m_DisplayLayoutModel; diff --git a/GUI/Model/GlobalWSWizardModel.cxx b/GUI/Model/GlobalWSWizardModel.cxx new file mode 100644 index 00000000..889703ef --- /dev/null +++ b/GUI/Model/GlobalWSWizardModel.cxx @@ -0,0 +1,63 @@ +#include "GlobalWSWizardModel.h" +#include "GlobalUIModel.h" +#include "IRISApplication.h" + + + +GlobalWSWizardModel::GlobalWSWizardModel() +{ +} + +void GlobalWSWizardModel::SetParentModel(GlobalUIModel *model) +{ + m_Parent = model; + m_Driver = m_Parent->GetDriver(); + m_GlobalState = m_Driver->GetGlobalState(); + + // Layer changes are rebroadcast as model changes, causing all child + // models to update themselves. + Rebroadcast(m_Driver, LayerChangeEvent(), ModelUpdateEvent()); + } + +void GlobalWSWizardModel::OnGlobalWSModeEnter() +{ + //// Initialize the image data + m_Driver->InitializeJOINImageData( + m_Driver->GetGlobalState()->GetSegmentationROISettings(), 1, //1: GWS; todo: use enum for selection state of CnJ + NULL, + m_Parent->GetProgressCommand()); + + m_Driver->SetCurrentImageDataToJOIN(); + //m_GlobalState->SetToolbarMode(CROSSHAIRS_MODE); //disables JoinDataPanel AND JoinInteraction + m_GlobalState->SetToolbarMode(GWSJOIN_MODE); //only disables JoinDataPanel + m_Parent->SetSegmentationVisibility(true); +} + +void GlobalWSWizardModel::OnCancelSegmentation() +{ + // Return to IRIS mode + m_Driver->SetCurrentImageDataToIRIS(); + m_Driver->ReleaseJOINImageData(); + + m_GlobalState->SetToolbarMode(CROSSHAIRS_MODE); //disables JoinInteraction + m_Parent->SetSegmentationVisibility(true); +} + +void GlobalWSWizardModel::OnFinishGWS() +{ + // Update IRIS with JOIN images + m_Driver->UpdateIRISWithJOINImageData(NULL); + + // Set an undo point + m_Driver->StoreUndoPoint("Automatic Segmentation"); + + // Return to IRIS mode + m_Driver->SetCurrentImageDataToIRIS(); + m_Driver->ReleaseJOINImageData(); + + m_GlobalState->SetToolbarMode(CROSSHAIRS_MODE); //disables JoinInteraction + m_Parent->SetSegmentationVisibility(true); +} + + + diff --git a/GUI/Model/GlobalWSWizardModel.h b/GUI/Model/GlobalWSWizardModel.h new file mode 100644 index 00000000..2a737baa --- /dev/null +++ b/GUI/Model/GlobalWSWizardModel.h @@ -0,0 +1,40 @@ +#ifndef GLOBALWSWIZARDMODEL_H +#define GLOBALWSWIZARDMODEL_H + +#include "SNAPCommon.h" +#include "AbstractModel.h" +#include "GlobalState.h" + +class GlobalUIModel; +class IRISApplication; + +class GlobalWSWizardModel : public AbstractModel +{ +public: + + irisITKObjectMacro(GlobalWSWizardModel, AbstractModel) + + void SetParentModel(GlobalUIModel *model); + irisGetMacro(Parent, GlobalUIModel *) + + /** Called when first displaying the GWS wizard */ + void OnGlobalWSModeEnter(); + + /** Cancel segmentation and return to IRIS */ + void OnCancelSegmentation(); + + /** Copy segmentation and return to IRIS */ + void OnFinishGWS(); + +protected: + GlobalWSWizardModel(); + virtual ~GlobalWSWizardModel() {} + + // Parent model + GlobalUIModel *m_Parent; + IRISApplication *m_Driver; + GlobalState *m_GlobalState; + +}; + +#endif // GLOBALWSWIZARDMODEL_H diff --git a/GUI/Model/JoinModel.cxx b/GUI/Model/JoinModel.cxx new file mode 100644 index 00000000..8f1c5a8f --- /dev/null +++ b/GUI/Model/JoinModel.cxx @@ -0,0 +1,103 @@ +#include "JoinModel.h" +#include "GlobalState.h" +#include "IRISApplication.h" +#include "JOINImageData.h" + +JoinModel::JoinModel(){ + } + +void JoinModel::SetParent(GenericSliceModel *parent){ + // Set up the parent + m_Parent = parent; + } + +void JoinModel::ComputeMousePosition(const Vector3f &xSlice){ + // Only when an image is loaded + if(!m_Parent->GetDriver()->IsMainImageLoaded()) + return; + + // Compute the new cross-hairs position in image space + Vector3f xCross = m_Parent->MapSliceToImage(xSlice); + + // Round the cross-hairs position down to integer + Vector3i xCrossInteger = to_int(xCross); + + // Make sure that the cross-hairs position is within bounds by clamping + // it to image dimensions + Vector3i xSize = + to_int(m_Parent->GetDriver()->GetCurrentImageData()->GetVolumeExtents()); + + Vector3ui newpos = to_unsigned_int( + xCrossInteger.clamp(Vector3i(0),xSize - Vector3i(1))); + + if(newpos != m_MousePosition || m_MouseInside == false) + { + m_MousePosition = newpos; + m_MouseInside = true; + //InvokeEvent(PaintbrushMovedEvent()); + } + } + +bool +JoinModel +::ProcessPushEvent(const Vector3f &xSlice, bool reverse_mode, bool ctrl){ + // Compute the mouse position + ComputeMousePosition(xSlice); + + if(ctrl){ + processPickLabel(reverse_mode); + } + else { + // Check if the right button was pressed + if(processCnJ(reverse_mode)){ + //add undo point (todo: Undo has no effect so far) + // m_Parent->GetDriver()->StoreUndoPoint("Click'n'Join"); // commented as long as it has no effec except for taking single-threaded processing time according to the size of the loaded data + //fire SegmentationChangeEvent for e.g. 3D view Update + m_Parent->GetDriver()->InvokeEvent(SegmentationChangeEvent()); + } + } + + ////do not eat event, i.e. do cursor chasing; only works for LMB not for RMB!!! + return false; + } + +void JoinModel::ProcessLeaveEvent(){ + m_MouseInside = false; + } + +void +JoinModel::processPickLabel(bool reverse_mode){ + + // Get the global objects + IRISApplication *driver = m_Parent->GetDriver(); + GlobalState *gs = driver->GetGlobalState(); + + // Get Join destination image + JdstImageWrapper *jdst = driver->GetJOINImageData()->GetJdst(); + JdstImageWrapper::ImageType::ValueType JdstClickPV= jdst->GetImage()->GetPixel(to_itkIndex(m_MousePosition)); + + if(!reverse_mode){ + // ColorLabelTable::ValidLabelConstIterator pos = + // driver->GetColorLabelTable()->GetValidLabels().find(JdstClickPV); + // gs->SetDrawingColorLabel(pos); + gs->SetDrawingColorLabel(JdstClickPV); + } + else{ + DrawOverFilter dof = gs->GetDrawOverFilter(); + dof.CoverageMode = PAINT_OVER_ONE; + dof.DrawOverLabel = JdstClickPV; + gs->SetDrawOverFilter(dof); + } + } + +bool +JoinModel::processCnJ(bool reverse_mode){ + // Get the global objects + IRISApplication *driver = m_Parent->GetDriver(); + + // Flag to see if anything was changed + bool flagUpdate= driver->ExecuteCnJCopy(to_itkIndex(m_MousePosition)); + + return flagUpdate; + } + diff --git a/GUI/Model/JoinModel.h b/GUI/Model/JoinModel.h new file mode 100644 index 00000000..ff75a100 --- /dev/null +++ b/GUI/Model/JoinModel.h @@ -0,0 +1,43 @@ +#ifndef JOINMODEL_H +#define JOINMODEL_H + +#include +#include "AbstractModel.h" +#include "GenericSliceModel.h" + +class GenericSliceModel; + +class JoinModel : public AbstractModel +{ +public: + irisITKObjectMacro(JoinModel, AbstractModel) + + irisGetMacro(Parent, GenericSliceModel *) + + void SetParent(GenericSliceModel *); + + bool ProcessPushEvent(const Vector3f &xSlice, bool reverse_mode, bool ctrl); + + void ProcessLeaveEvent(); + +protected: + + // Mouse position in voxel coordinates + Vector3ui m_MousePosition; + bool m_MouseInside; + + // Private constructor stuff + JoinModel(); + virtual ~JoinModel() {} + + void ComputeMousePosition(const Vector3f &xSlice); + + void processPickLabel(bool reverse_mode); + bool processCnJ(bool reverse_mode); + + // Parent model + GenericSliceModel *m_Parent; + +}; + +#endif // JOINMODEL_H diff --git a/GUI/Model/PaintbrushModel.cxx b/GUI/Model/PaintbrushModel.cxx index 7a3b74af..650b9c7d 100644 --- a/GUI/Model/PaintbrushModel.cxx +++ b/GUI/Model/PaintbrushModel.cxx @@ -30,7 +30,6 @@ class BrushWatershedPipeline gmf = GMFType::New(); gmf->SetInput(adf->GetOutput()); wf = WFType::New(); - wf->SetInput(gmf->GetOutput()); } void PrecomputeWatersheds( @@ -38,39 +37,55 @@ class BrushWatershedPipeline LabelImageType *label, itk::ImageRegion<3> region, itk::Index<3> vcenter, - size_t smoothing_iter) + size_t smoothing_iter, + bool direct) { - this->region = region; + this->m_region = region; // Get the offset of vcenter in the region - if(region.IsInside(vcenter)) + // if(m_region.IsInside(vcenter)) + // for(size_t d = 0; d < 3; d++) + // this->vcenter[d] = vcenter[d] - m_region.GetIndex()[d]; + // else for(size_t d = 0; d < 3; d++) - this->vcenter[d] = vcenter[d] - region.GetIndex()[d]; - else - for(size_t d = 0; d < 3; d++) - this->vcenter[d] = region.GetSize()[d] / 2; + this->vcenter[d] = m_region.GetSize()[d] / 2; // Create a backup of the label image LROIType::Pointer lroi = LROIType::New(); lroi->SetInput(label); - lroi->SetRegionOfInterest(region); + lroi->SetRegionOfInterest(m_region); lroi->Update(); - lsrc = lroi->GetOutput(); + lsrc= lroi->GetOutput(); lsrc->DisconnectPipeline(); // Initialize the watershed pipeline roi->SetInput(grey); - roi->SetRegionOfInterest(region); + roi->SetRegionOfInterest(m_region); adf->SetNumberOfIterations(smoothing_iter); + if(direct) + wf->SetInput(adf->GetOutput()); + else + wf->SetInput(gmf->GetOutput()); // Set the initial level to lowest possible - to get all watersheds wf->SetLevel(1.0); wf->Update(); } +void RecomputeLabelBackup(LabelImageType *label)//, itk::ImageRegion<3> region) + { + // Create a backup of the label image + LROIType::Pointer lroi = LROIType::New(); + lroi->SetInput(label); + lroi->SetRegionOfInterest(m_region); + lroi->Update(); + lsrc= lroi->GetOutput(); + lsrc->DisconnectPipeline(); + } + void RecomputeWatersheds(double level) { - // Reupdate the filter with new level + // Reupdate the filter with new level if a precomputed input already exists for this view wf->SetLevel(level); wf->Update(); } @@ -100,7 +115,7 @@ class BrushWatershedPipeline typedef itk::ImageRegionIterator LIter; WIter wit(wf->GetOutput(), wf->GetOutput()->GetBufferedRegion()); LIter sit(lsrc, lsrc->GetBufferedRegion()); - LIter tit(ltrg, region); + LIter tit(ltrg, m_region); for(; !wit.IsAtEnd(); ++sit,++tit,++wit) { LabelType pxLabel = sit.Get(); @@ -138,7 +153,7 @@ class BrushWatershedPipeline GMFType::Pointer gmf; WFType::Pointer wf; - itk::ImageRegion<3> region; + itk::ImageRegion<3> m_region; LabelImageType::Pointer lsrc; itk::Index<3> vcenter; }; @@ -152,6 +167,7 @@ PaintbrushModel::PaintbrushModel() { m_ReverseMode = false; m_Watershed = new BrushWatershedPipeline(); + m_PrecomputedWS= false; m_ContextLayerId = (unsigned long) -1; m_IsEngaged = false; } @@ -241,6 +257,49 @@ bool PaintbrushModel::TestInside(const Vector3d &x, const PaintbrushSettings &ps } } +bool //returns false if no voxel changed, this should not be used to accept the wheel event! +PaintbrushModel +::ProcessWheelEvent(int delta) + { + + ////Event is only issued if mouse is inside the view, however each view has its own watershed pipeline! The region is only needed in PrecomputeWatersheds for the WS input. So just keep the input for any reordering and do nothing if there is no precomputed input. + + ////return if there is no precomp WS + if(!m_PrecomputedWS) + return false; + + // Get the paintbrush properties (TODO: should we own them?) + PaintbrushSettings pbs = + m_Parent->GetDriver()->GetGlobalState()->GetPaintbrushSettings(); + + ////inc./dec. level by 1% depending on wheel direction + if(delta > 0){ + m_level+= 0.01; + if(m_level > 1) + m_level= 1.0; + } + else{ + m_level-= 0.01; + if(m_level < 0) + m_level= 0.0; + } + m_Parent->GetDriver()->GetGlobalState()->SetPaintbrushSettings(pbs); + + if(pbs.mode == PAINTBRUSH_WATERSHED) + { + fprintf(stderr, "Regrouping watersheds! (view %d, level %f)\n", m_Parent->GetId(), m_level); + ////Update the brush state + if(UpdateBrush()){ + ////tell the GUI to repaint the segmentation and that the 3D view can be updated + m_Parent->GetDriver()->StoreUndoPoint("Dynamic Granularity change"); + m_Parent->GetDriver()->InvokeEvent(SegmentationChangeEvent()); + return true; + } + } + ////do not use event for cursor chasing, but for GUI hint + return false; + } + bool PaintbrushModel ::ProcessPushEvent(const Vector3f &xSlice, const Vector2ui &gridCell, bool reverse_mode) @@ -259,6 +318,7 @@ ::ProcessPushEvent(const Vector3f &xSlice, const Vector2ui &gridCell, bool rever // Compute the mouse position ComputeMousePosition(xSlice); + m_level= pbs.watershed.level; // Check if the right button was pressed ApplyBrush(reverse_mode, false); @@ -349,6 +409,13 @@ bool PaintbrushModel::ProcessMouseMoveEvent(const Vector3f &xSlice) return true; } +// bool PaintbrushModel::ProcessMouseEnterEvent() +// { +// if(m_PrecomputedWS == false) +// return false; +// m_Watershed->RecomputeLabelBackup(m_Parent->GetDriver()->GetCurrentImageData()->GetSegmentation()->GetImage()); +// return true; +// } bool PaintbrushModel::ProcessMouseLeaveEvent() { @@ -416,6 +483,7 @@ PaintbrushModel::ApplyBrush(bool reverse_mode, bool dragging) // Crop the region by the buffered region xTestRegion.Crop(imgLabel->GetImage()->GetBufferedRegion()); + m_OldxTestRegion= xTestRegion; // Flag to see if anything was changed bool flagUpdate = false; @@ -430,13 +498,19 @@ PaintbrushModel::ApplyBrush(bool reverse_mode, bool dragging) if(!context_layer) context_layer = gid->GetMain(); + fprintf(stderr, "Precomputing watersheds! (view %d, level %f)\n", m_Parent->GetId(), m_level); // Precompute the watersheds m_Watershed->PrecomputeWatersheds( context_layer->GetDefaultScalarRepresentation()->GetCommonFormatImage(), driver->GetCurrentImageData()->GetSegmentation()->GetImage(), - xTestRegion, to_itkIndex(m_MousePosition), pbs.watershed.smooth_iterations); + xTestRegion, to_itkIndex(m_MousePosition), pbs.watershed.smooth_iterations, pbs.direct); + + m_Watershed->RecomputeWatersheds(m_level); + m_PrecomputedWS= true; + return m_Watershed->UpdateLabelImage( + driver->GetCurrentImageData()->GetSegmentation()->GetImage(), + gs->GetDrawOverFilter().CoverageMode, gs->GetDrawingColorLabel(), gs->GetDrawOverFilter().DrawOverLabel); - m_Watershed->RecomputeWatersheds(pbs.watershed.level); } // Shift vector (different depending on whether the brush has odd/even diameter @@ -461,13 +535,13 @@ PaintbrushModel::ApplyBrush(bool reverse_mode, bool dragging) continue; // Check if the pixel is in the watershed - if(flagWatershed) - { - LabelImageWrapper::ImageType::IndexType idxoff = to_itkIndex( - Vector3l(idx.GetIndex()) - Vector3l(xTestRegion.GetIndex().GetIndex())); - if(!m_Watershed->IsPixelInSegmentation(idxoff)) - continue; - } + // if(flagWatershed) + // { + // LabelImageWrapper::ImageType::IndexType idxoff = to_itkIndex( + // Vector3l(idx.GetIndex()) - Vector3l(xTestRegion.GetIndex().GetIndex())); + // if(!m_Watershed->IsPixelInSegmentation(idxoff)) + // continue; + // } // Paint the pixel LabelType pxLabel = it.Get(); @@ -508,6 +582,23 @@ PaintbrushModel::ApplyBrush(bool reverse_mode, bool dragging) return flagUpdate; } +bool +PaintbrushModel::UpdateBrush() + { + // Get the global objects + IRISApplication *driver = m_Parent->GetDriver(); + GlobalState *gs = driver->GetGlobalState(); + + // Get the segmentation image + LabelImageWrapper *imgLabel = driver->GetCurrentImageData()->GetSegmentation(); + + // Get the paintbrush properties + PaintbrushSettings pbs = gs->GetPaintbrushSettings(); + + m_Watershed->RecomputeWatersheds(m_level); + + return m_Watershed->UpdateLabelImage(imgLabel->GetImage(), gs->GetDrawOverFilter().CoverageMode, gs->GetDrawingColorLabel(), gs->GetDrawOverFilter().DrawOverLabel); + } Vector3f PaintbrushModel::GetCenterOfPaintbrushInSliceSpace() { diff --git a/GUI/Model/PaintbrushModel.h b/GUI/Model/PaintbrushModel.h index c663550e..e3a18c3f 100644 --- a/GUI/Model/PaintbrushModel.h +++ b/GUI/Model/PaintbrushModel.h @@ -3,6 +3,7 @@ #include "AbstractModel.h" #include "GlobalState.h" +#include "GenericImageData.h" //contains def. of LabelImageWrapper class GenericSliceModel; class BrushWatershedPipeline; @@ -22,11 +23,13 @@ class PaintbrushModel : public AbstractModel FIRES(PaintbrushMovedEvent) + bool ProcessWheelEvent(int delta); bool ProcessPushEvent(const Vector3f &xSlice, const Vector2ui &gridCell, bool reverse_mode); bool ProcessDragEvent(const Vector3f &xSlice, const Vector3f &xSliceLast, double pixelsMoved, bool release); bool ProcessMouseMoveEvent(const Vector3f &xSlice); + //bool ProcessMouseEnterEvent(); bool ProcessMouseLeaveEvent(); void AcceptAtCursor(); @@ -45,6 +48,12 @@ class PaintbrushModel : public AbstractModel Vector3ui m_MousePosition; bool m_MouseInside; + //for WS updates + double m_level; + bool m_PrecomputedWS; + Vector3f m_offset; + LabelImageWrapper::ImageType::RegionType m_OldxTestRegion; + // Mouse position in slice coordinates from which we need to draw the // next segment Vector3f m_LastApplyX; @@ -62,6 +71,7 @@ class PaintbrushModel : public AbstractModel void ComputeMousePosition(const Vector3f &xSlice); bool ApplyBrush(bool reverse_mode, bool dragging); + bool UpdateBrush(); bool TestInside(const Vector2d &x, const PaintbrushSettings &ps); bool TestInside(const Vector3d &x, const PaintbrushSettings &ps); diff --git a/GUI/Model/PaintbrushSettingsModel.cxx b/GUI/Model/PaintbrushSettingsModel.cxx index 6785a21c..606e6e07 100644 --- a/GUI/Model/PaintbrushSettingsModel.cxx +++ b/GUI/Model/PaintbrushSettingsModel.cxx @@ -26,6 +26,10 @@ PaintbrushSettingsModel::PaintbrushSettingsModel() wrapStructMemberAsSimpleProperty( m_PaintbrushSettingsModel, offsetof(PaintbrushSettings, chase)); + m_DirecWSModel = + wrapStructMemberAsSimpleProperty( + m_PaintbrushSettingsModel, offsetof(PaintbrushSettings, direct)); + // The paintbrush size model requires special processing, so it is implemeted // using a getter/setter pair m_BrushSizeModel = wrapGetterSetterPairAsProperty( @@ -70,7 +74,7 @@ ::GetBrushSizeValueAndRange(int &value, NumericValueRange *domain) // Round just in case value = (int) (pbs.radius * 2 + 0.5); if(domain) - domain->Set(1, 40, 1); + domain->Set(1, 1000, 1); return true; } diff --git a/GUI/Model/PaintbrushSettingsModel.h b/GUI/Model/PaintbrushSettingsModel.h index bdb24196..7e02f8d9 100644 --- a/GUI/Model/PaintbrushSettingsModel.h +++ b/GUI/Model/PaintbrushSettingsModel.h @@ -25,6 +25,7 @@ class PaintbrushSettingsModel : public AbstractModel irisGetMacro(ChaseCursorModel, AbstractSimpleBooleanProperty *) irisGetMacro(AdaptiveModeModel, AbstractSimpleBooleanProperty *) + irisGetMacro(DirecWSModel, AbstractSimpleBooleanProperty *) irisGetMacro(ThresholdLevelModel, AbstractRangedDoubleProperty *) irisGetMacro(SmoothingIterationsModel, AbstractRangedIntProperty *) @@ -53,6 +54,8 @@ class PaintbrushSettingsModel : public AbstractModel SmartPtr m_AdaptiveModeModel; bool GetAdaptiveModeValue(bool &value); + SmartPtr m_DirecWSModel; + SmartPtr m_ThresholdLevelModel; bool GetThresholdLevelValueAndRange(double &value, NumericValueRange *domain); void SetThresholdLevelValue(double value); diff --git a/GUI/Model/UIState.h b/GUI/Model/UIState.h index 5b69d1c2..29e5bfdd 100644 --- a/GUI/Model/UIState.h +++ b/GUI/Model/UIState.h @@ -16,6 +16,8 @@ enum UIState { UIF_UNSAVED_CHANGES, UIF_MESH_SAVEABLE, UIF_SNAKE_MODE, + UIF_JOIN_MODE, + UIF_NOT_SNAKE_OR_JOIN_MODE, UIF_LEVEL_SET_ACTIVE, UIF_MULTIPLE_BASE_LAYERS // i.e., more than one non-sticky layer }; diff --git a/GUI/Qt/Components/CursorInspector.cxx b/GUI/Qt/Components/CursorInspector.cxx index e0ef2538..96a98e21 100644 --- a/GUI/Qt/Components/CursorInspector.cxx +++ b/GUI/Qt/Components/CursorInspector.cxx @@ -98,7 +98,7 @@ CursorInspector::CursorInspector(QWidget *parent) : ui->tableVoxelUnderCursor->setModel(m_CurrentVoxelItemModel); ui->tableVoxelUnderCursor->setAlternatingRowColors(true); - ui->tableVoxelUnderCursor->setFixedWidth(160); + //ui->tableVoxelUnderCursor->setFixedWidth(160);//let table expand ui->tableVoxelUnderCursor->setFixedHeight(120); ui->tableVoxelUnderCursor->setContextMenuPolicy(Qt::CustomContextMenu); diff --git a/GUI/Qt/Components/GlobalWSWizardPanel.cxx b/GUI/Qt/Components/GlobalWSWizardPanel.cxx new file mode 100644 index 00000000..c163c344 --- /dev/null +++ b/GUI/Qt/Components/GlobalWSWizardPanel.cxx @@ -0,0 +1,363 @@ +/* +part of Click'n'Join mode, which was contributed by Roman Grothausmann +*/ + +#include "GlobalWSWizardPanel.h" +#include "ui_GlobalWSWizardPanel.h" +#include "GlobalUIModel.h" +#include "GlobalWSWizardModel.h" +#include "QtCursorOverride.h" +#include "QtDoubleSliderWithEditorCoupling.h" +#include "IRISException.h" +#include +#include "IRISApplication.h" +#include "GenericImageData.h" +#include "JOINImageData.h" +#include "DisplayLayoutModel.h" //access tiled/stacked view mode +#include "ImageIODelegates.h" +#include "ImageIOWizard.h" +#include "ImageIOWizardModel.h" + +#include +#include +#include +#include +#include + + +void FilterEventHandlerITK(itk::Object *caller, const itk::EventObject &event, void*){ + + const itk::ProcessObject* filter = static_cast(caller); + + if(itk::ProgressEvent().CheckEvent(&event)) + fprintf(stderr, "\r%s progress: %5.1f%%", filter->GetNameOfClass(), 100.0 * filter->GetProgress());//stderr is flushed directly + else if(itk::EndEvent().CheckEvent(&event)) + std::cerr << std::endl << std::flush; + } + + +// TODO: move this into a separate file!!!! +class WatershedPipeline{ +public: + typedef itk::Image GreyImageType; + typedef itk::Image JsrcImageType; + typedef itk::Image WsrcImageType; + typedef itk::Image WatershedImageType; + + WatershedPipeline(){ + + itk::CStyleCommand::Pointer eventCallbackITK; + eventCallbackITK = itk::CStyleCommand::New(); + eventCallbackITK->SetCallback(FilterEventHandlerITK); + + adf = ADFType::New(); + adf->AddObserver(itk::ProgressEvent(), eventCallbackITK); + adf->AddObserver(itk::EndEvent(), eventCallbackITK); + gmf = GMFType::New(); + gmf->AddObserver(itk::ProgressEvent(), eventCallbackITK); + gmf->AddObserver(itk::EndEvent(), eventCallbackITK); + gmf->SetInput(adf->GetOutput()); + wf = WFType::New(); + wf->AddObserver(itk::ProgressEvent(), eventCallbackITK); + wf->AddObserver(itk::EndEvent(), eventCallbackITK); + cif = CIFType::New(); + cif->SetInput(wf->GetOutput()); + } + + WsrcImageType* ComputeWSImage( + GreyImageType *grey, + double cParam, size_t sIter, + bool direct){ + + //// Initialize the watershed pipeline + adf->SetInput(grey); + adf->SetNumberOfIterations(sIter); + adf->SetConductanceParameter(cParam); + + WsrcImageType* wsImage; + + if(direct){ + adf->UpdateLargestPossibleRegion(); + wsImage= adf->GetOutput(); + } + else{ + gmf->UpdateLargestPossibleRegion(); + wsImage= gmf->GetOutput(); + } + + wf->SetInput(wsImage);//causes recomp even when no params were chanaged -> todo: optimize by setting input only if it changed + return(wsImage); + } + + JsrcImageType* PrecomputeWatersheds(double iThr, double iLevel){ + + wf->SetThreshold(iThr); + wf->SetLevel(iLevel); + cif->UpdateLargestPossibleRegion(); //needed because RequestedRegion can be bigger than BufferedRegion in consecutive runs of GWS (http://itk.org/ITKExamples/src/Core/Common/ReRunPipelineWithChangingLargestPossibleRegion/Documentation.html) + + return(cif->GetOutput()); + } + + void RecomputeWatersheds(double level){ + // Reupdate the filter with new level + wf->SetLevel(level); + cif->Update(); + } + +private: + typedef itk::GradientAnisotropicDiffusionImageFilter ADFType; + typedef itk::GradientMagnitudeImageFilter GMFType; + typedef itk::WatershedImageFilter WFType; + typedef itk::CastImageFilter CIFType; + + ADFType::Pointer adf; + GMFType::Pointer gmf; + WFType::Pointer wf; + CIFType::Pointer cif; + + }; + + + +GlobalWSWizardPanel::GlobalWSWizardPanel(QWidget *parent) : + QWidget(parent), + ui(new Ui::GlobalWSWizardPanel){ + ui->setupUi(this); + + connect(ui->inWSLevel, SIGNAL(valueChanged(double)), this, SLOT(on_inWSLevel_valueChanged(double))); + m_old_value= 0; + + m_Watershed = new WatershedPipeline(); + + this->addAction(ui->actionIncreaseWSLevel); + this->addAction(ui->actionDecreaseWSLevel); + } + +GlobalWSWizardPanel::~GlobalWSWizardPanel(){ + delete ui; + } + +void GlobalWSWizardPanel::SetModel(GlobalUIModel *model){ + // Store and pass on the models + m_ParentModel = model; + m_Model = model->GetGlobalWSWizardModel(); + + activateOnFlag(ui->btnCopySeg, m_ParentModel, UIF_JOIN_MODE); + activateOnFlag(ui->btnClearSeg, m_ParentModel, UIF_JOIN_MODE); + } + +void GlobalWSWizardPanel::Initialize(){ + // Initialize the model + m_Model->OnGlobalWSModeEnter(); + + // Go to the right page + ui->stack->setCurrentWidget(ui->pgPreproc); + } + +void GlobalWSWizardPanel::on_stack_currentChanged(int page){ + // The stack at the top follows the stack at the bottom + ui->stackStepInfo->setCurrentIndex(page); + } + +void GlobalWSWizardPanel::on_btnCancel_clicked(){ + // Tell the model to return to initialization state + m_Model->OnCancelSegmentation(); + + // Tell parent to hide this window + emit wizardFinished(); + } + +void GlobalWSWizardPanel::on_btnNextPreproc_clicked(){ + // Get the global objects + IRISApplication *driver = m_ParentModel->GetDriver(); + GlobalState *gs = driver->GetGlobalState(); + + try + { + // Handle cursor + QtCursorOverride curse(Qt::WaitCursor); + + driver->GetJOINImageData()->SetWsrc( + m_Watershed->ComputeWSImage( + driver->GetCurrentImageData()->GetMain()->GetDefaultScalarRepresentation()->GetCommonFormatImage(), + ui->inConductance->value()/100.0, ui->inSmoothingIter->value(), + ui->chkGlobalWSDirect->isChecked() + ) + ); + // set tiled layout to ease understanding the interaction mode + driver->GetJOINImageData()->SetWsrcSticky(false); + m_ParentModel->SetWsrcVisibility(true); + //m_ParentModel->SetJsrcVisibility(false);//let user decide + //m_ParentModel->SetJdstVisibility(false);//let user decide + m_ParentModel->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_STACKED);//only when coming from stacked view will the tiled view get reorganized + m_ParentModel->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_TILED); + + // Move to the range page + ui->stack->setCurrentWidget(ui->pgWSRange); + } + catch(IRISException &exc) + { + QMessageBox::warning(this, "ITK-SNAP", exc.what(), QMessageBox::Ok); + } + } + +void GlobalWSWizardPanel::on_btnWSRangeNext_clicked(){ + + // Get the global objects + IRISApplication *driver = m_ParentModel->GetDriver(); + GlobalState *gs = driver->GetGlobalState(); + + try + { + // Handle cursor + QtCursorOverride curse(Qt::WaitCursor); + + driver->GetJOINImageData()->SetJsrc( + m_Watershed->PrecomputeWatersheds(ui->inWSMin->value()/100.0, ui->inWSMax->value()/100.0) + ); + // set tiled layout to ease understanding the interaction mode + driver->GetJOINImageData()->SetJdstSticky(false); + driver->GetJOINImageData()->SetWsrcSticky(true); + m_ParentModel->SetWsrcVisibility(false); + m_ParentModel->SetJsrcVisibility(true);//make sure Jsrc is visible + m_ParentModel->SetJdstVisibility(true);//make sure Jdst is visible + m_ParentModel->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_STACKED);//only when coming from stacked view will the tiled view get reorganized + m_ParentModel->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_TILED); + + std::cerr << "Changing WS to level: " << ui->inWSLevel->value()/100.0 << std::flush; + m_Watershed->RecomputeWatersheds(ui->inWSLevel->value()/100.0); + std::cerr << " Recomputed WS level." << std::flush << std::endl; + driver->InvokeEvent(SegmentationChangeEvent()); + + ui->stack->setCurrentWidget(ui->pgDynWSLevel); + } + catch(IRISException &exc) + { + QMessageBox::warning(this, "ITK-SNAP", exc.what(), QMessageBox::Ok); + } + + } + +void GlobalWSWizardPanel::on_inWSLevel_valueChanged(double value){ + + // Get the global objects + IRISApplication *driver = m_ParentModel->GetDriver(); + GlobalState *gs = driver->GetGlobalState(); + + GenericImageData *gid = driver->GetCurrentImageData(); + + if(value != m_old_value){ + try + { + std::cerr << "Changing WS to level: " << value/100.0 << std::flush; + m_Watershed->RecomputeWatersheds(value/100.0); + std::cerr << " Recomputed WS level." << std::flush << std::endl; + driver->InvokeEvent(SegmentationChangeEvent()); + } + catch(IRISException &exc) + { + QMessageBox::warning(this, "ITK-SNAP", exc.what(), QMessageBox::Ok); + } + m_old_value= value; + } + + } + +void GlobalWSWizardPanel::on_btnGWSfinish_clicked(){ + // Tell the model to return to initialization state + m_Model->OnFinishGWS(); + + // Tell parent to hide this window + emit wizardFinished(); + } + +void GlobalWSWizardPanel::on_actionIncreaseWSLevel_triggered(){ + double value= ui->inWSLevel->value() + 0.1; + ui->inWSLevel->setValue(value); + emit ui->inWSLevel->spinnerValueChanged(value); //def in GUI/Qt/Components/QDoubleSliderWithEditor.h + } + +void GlobalWSWizardPanel::on_actionDecreaseWSLevel_triggered(){ + double value= ui->inWSLevel->value() - 0.1; + ui->inWSLevel->setValue(value); + emit ui->inWSLevel->spinnerValueChanged(value); + } + +void GlobalWSWizardPanel::on_btnCopySeg_clicked(){ + IRISApplication *driver = m_ParentModel->GetDriver(); + driver->CopySegementationToJdst( + driver->GetGlobalState()->GetSegmentationROISettings(), + m_ParentModel->GetProgressCommand()); + + driver->InvokeEvent(SegmentationChangeEvent()); + m_ParentModel->SetSegmentationVisibility(true); + } + +void GlobalWSWizardPanel::on_btnClearSeg_clicked(){ + IRISApplication *driver = m_ParentModel->GetDriver(); + driver->ClearJdst(); + + driver->InvokeEvent(SegmentationChangeEvent()); + m_ParentModel->SetSegmentationVisibility(true); + } + +void GlobalWSWizardPanel::on_btnLoadFromFile_clicked(){///better make it a choose overlay button as ROI cropping has already happend + // not working yet + // Create a model for IO + SmartPtr delegate = LoadOverlayImageDelegate::New(); + delegate->Initialize(m_ParentModel->GetDriver()); + SmartPtr model = ImageIOWizardModel::New(); + model->InitializeForLoad(m_ParentModel, delegate, + "GWSImage", "GWS Source Image"); + + // Execute the IO wizard + ImageIOWizard wiz(this); + wiz.SetModel(model); + wiz.exec(); + } + +void GlobalWSWizardPanel::on_btnWSRangeBack_clicked(){ + try + { + // Handle cursor + QtCursorOverride curse(Qt::WaitCursor); + + // Move to the range page + ui->stack->setCurrentWidget(ui->pgPreproc); + + // reset tiled layout + m_ParentModel->GetDriver()->GetJOINImageData()->SetWsrcSticky(true); + m_ParentModel->SetWsrcVisibility(false); + //m_ParentModel->SetJsrcVisibility(false);//let user decide + //m_ParentModel->SetJdstVisibility(false);//let user decide + m_ParentModel->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_STACKED); + } + catch(IRISException &exc) + { + QMessageBox::warning(this, "ITK-SNAP", exc.what(), QMessageBox::Ok); + } + } + +void GlobalWSWizardPanel::on_btnJoinBack_clicked(){ + try + { + // Handle cursor + QtCursorOverride curse(Qt::WaitCursor); + + // Move to the range page + ui->stack->setCurrentWidget(ui->pgWSRange); + + // reset tiled layout + m_ParentModel->GetDriver()->GetJOINImageData()->SetJdstSticky(true); + m_ParentModel->GetDriver()->GetJOINImageData()->SetWsrcSticky(false); + m_ParentModel->SetWsrcVisibility(true); + //m_ParentModel->SetJsrcVisibility(false);//let user decide + //m_ParentModel->SetJdstVisibility(false);//let user decide + m_ParentModel->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_STACKED);//only when coming from stacked view will the tiled view get reorganized + m_ParentModel->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_TILED); + } + catch(IRISException &exc) + { + QMessageBox::warning(this, "ITK-SNAP", exc.what(), QMessageBox::Ok); + } + } diff --git a/GUI/Qt/Components/GlobalWSWizardPanel.h b/GUI/Qt/Components/GlobalWSWizardPanel.h new file mode 100644 index 00000000..bc2e3de4 --- /dev/null +++ b/GUI/Qt/Components/GlobalWSWizardPanel.h @@ -0,0 +1,84 @@ +/* +part of Click'n'Join mode, which was contributed by Roman Grothausmann +*/ + +#ifndef GLOBALWSWIZARDPANEL_H +#define GLOBALWSWIZARDPANEL_H + +#include +#include + +namespace Ui { +class GlobalWSWizardPanel; +} + +class GlobalUIModel; +class GlobalWSWizardModel; +class WatershedPipeline; + + + +class GlobalWSWizardPanel : public QWidget +{ + Q_OBJECT + +public: + explicit GlobalWSWizardPanel(QWidget *parent = 0); + ~GlobalWSWizardPanel(); + + void SetModel(GlobalUIModel *model); + + /** + Put the panel into the initial state, i.e., ready to perform preprocessing. + You must call this method before showing the panel. + */ + void Initialize(); + +signals: + + void wizardFinished(); + +private slots: + + void on_stack_currentChanged(int arg1); + + void on_btnCancel_clicked(); + + void on_btnNextPreproc_clicked(); + + void on_btnWSRangeNext_clicked(); + + void on_inWSLevel_valueChanged(double value); + + void on_btnGWSfinish_clicked(); + + void on_actionIncreaseWSLevel_triggered(); + + void on_actionDecreaseWSLevel_triggered(); + + void on_btnCopySeg_clicked(); + + void on_btnClearSeg_clicked(); + + void on_btnLoadFromFile_clicked(); + + void on_btnWSRangeBack_clicked(); + + void on_btnJoinBack_clicked(); + +private: + + GlobalUIModel *m_ParentModel; + GlobalWSWizardModel *m_Model; + + double m_old_value; + + Ui::GlobalWSWizardPanel *ui; + +protected: + + WatershedPipeline *m_Watershed; + +}; + +#endif // GLOBALWSWIZARDPANEL_H diff --git a/GUI/Qt/Components/GlobalWSWizardPanel.ui b/GUI/Qt/Components/GlobalWSWizardPanel.ui new file mode 100644 index 00000000..02894858 --- /dev/null +++ b/GUI/Qt/Components/GlobalWSWizardPanel.ui @@ -0,0 +1,794 @@ + + + GlobalWSWizardPanel + + + + 0 + 0 + 177 + 680 + + + + + 0 + 0 + + + + + 177 + 16777215 + + + + Form + + + * { +font-size: 12px; +} +QGroupBox { + background-origin: content; + margin-top: 15px; + font-weight: bold; + font-size: 12px; + font-family: helvetica; + color: rgb(30,30,160); + background-color: rgb(237,237,237); + padding: 5px; + border-radius: 4px; + border: 1px solid rgb(130,130,130); +} +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; +} + + + + + + 8 + + + 5 + + + + + + 0 + 60 + + + + Current Stage + + + + 0 + + + + + + 0 + 0 + + + + 0 + + + + + 0 + + + + + + 16777215 + 16777215 + + + + font-size:10px; + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Lucida Grande'; font-size:10px; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12px;">Step 1/3</span></p> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12px; font-weight:600;">Preprocessing</span></p></body></html> + + + true + + + + + + + + + 0 + + + + + + 16777215 + 16777215 + + + + font-size:10px; + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Lucida Grande'; font-size:10px; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12px;">Step 2/3</span></p> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:12px; font-weight:600;">Initialization</span></p></body></html> + + + true + + + + + + + + + 0 + + + + + + 16777215 + 16777215 + + + + font-size:10px; + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:10px; font-weight:400; font-style:normal;"> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:12px;">Step 3/3</span></p> +<p align="center" style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:12px; font-weight:600;">Click 'n' Join</span></p></body></html> + + + true + + + + + + + + + + + + + + + 0 + 0 + + + + Actions: + + + + 0 + + + + + + 0 + 0 + + + + 0 + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Should the current image be used as input for the GWS or another?</p></body></html> + + + true + + + + + + + Load from File + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + + 12 + + + 0 + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:12px; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13px;">Should the image be used directly for the GWS (direct) or a gradient-magnitude image (indirect)?</span></p></body></html> + + + true + + + + + + + + + + direct + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:12px; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:12px;">Adjust the smoothing filter parameters.</span></p></body></html> + + + true + + + + + + + Conductance + + + + + + + 100 + + + 0 + + + 50 + + + Qt::Horizontal + + + + + + + smoothing iterations + + + + + + + 1 + + + 200 + + + 10 + + + Qt::Horizontal + + + + + + + + 4 + + + 0 + + + + + false + + + + 0 + 0 + + + + + 64 + 16777215 + + + + Back + + + + + + + + 0 + 0 + + + + + 64 + 16777215 + + + + Next + + + + + + + + + + + + 0 + + + + + <html><head/><body><p>Adjust the initial parameters for the GWS. No watersheds between the &quot;Min. Depth&quot; and the &quot;Max. Depth&quot; will be available (the smaller this range the faster is the computation). Both parameters are a percentage of the height range of the GWS input image.</p></body></html> + + + true + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + Minimum Depth + + + + + + + + 0 + 0 + + + + 100 + + + Qt::Horizontal + + + + + + + Maximum Depth + + + + + + + 100 + + + 100 + + + Qt::Horizontal + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:12px; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13px;">Press 'Next' to start the computation of the hierarchy tree of Watersheds. This might take a while!</span></p></body></html> + + + true + + + + + + + + 4 + + + 0 + + + + + + 0 + 0 + + + + + 64 + 16777215 + + + + Back + + + + + + + + 0 + 0 + + + + + 64 + 16777215 + + + + Next + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + 0 + + + + + + 12 + + + 0 + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:12px; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:12px;">Click LMB to add labeled regions to the final segmentation with the current foreground Label, RMB to remove.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13px;">Use the slider or +/- to adjust the actual depth used for merging labels within the watershed hierarchy.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13px;">CTRL+LMB/RMB picks the FG/BG label from the final segmentation.</span></p></body></html> + + + true + + + + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + adjustable Level + + + + + + + 100 + + + 22 + + + Qt::Horizontal + + + + + + + QFrame::Plain + + + Qt::Horizontal + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Sans Serif'; font-size:12px; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'Lucida Grande'; font-size:13px;">&quot;Finish&quot; will copy the final segmentation according to the chosen draw-over mode (BG label).</span></p></body></html> + + + true + + + + + + + + 4 + + + 0 + + + + + + 0 + 0 + + + + + 64 + 16777215 + + + + Back + + + + + + + + 0 + 0 + + + + + 64 + 16777215 + + + + Finish + + + + + + + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + + + + + + + + + Copy Segmentation + + + + + + + Clear Segmentation + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + Cancel Segmentation + + + + + + + Increase WS Level + + + Increase Watershed Level + + + + + + + + + Decrease WS Level + + + Decrease Watershed Level + + + - + + + + + + QDoubleSliderWithEditor + QSlider +
QDoubleSliderWithEditor.h
+
+
+ + +
diff --git a/GUI/Qt/Components/JoinDataPanel.cxx b/GUI/Qt/Components/JoinDataPanel.cxx new file mode 100644 index 00000000..0290be87 --- /dev/null +++ b/GUI/Qt/Components/JoinDataPanel.cxx @@ -0,0 +1,182 @@ +/* +part of Click'n'Join mode, which was contributed by Roman Grothausmann +*/ + +#include "JoinDataPanel.h" +#include "ui_JoinDataPanel.h" + +#include "SNAPQtCommon.h" //findParentWidget +#include "GlobalUIModel.h" //GlobalUIModel +#include "JoinModel.h" +#include "GlobalState.h" //CROSSHAIRS_MODE +#include "IRISApplication.h" +#include "GenericImageData.h" //GetCurrentImageData()->GetImageRegion(); +#include "QtWidgetActivator.h" //activateOnFlag +#include "DisplayLayoutModel.h" //access tiled/stacked view mode +#include "ImageIODelegates.h" +#include "ImageIOWizard.h" +#include "ImageIOWizardModel.h" +#include "JOINImageData.h" + + +JoinDataPanel::JoinDataPanel(QWidget *parent) : + SNAPComponent(parent), + ui(new Ui::JoinDataPanel){ + ui->setupUi(this); + } + +JoinDataPanel::~JoinDataPanel(){ + delete ui; + } + +void JoinDataPanel::SetModel(GlobalUIModel *model){ + // Store the model + m_Model = model; + + activateOnNotFlag(ui->btnStartCnJ, m_Model, UIF_JOIN_MODE); + activateOnNotFlag(ui->inJoinType, m_Model, UIF_JOIN_MODE); + activateOnFlag(ui->instrText, m_Model, UIF_JOIN_MODE); + activateOnFlag(ui->btnFinishCnJ, m_Model, UIF_JOIN_MODE); + activateOnFlag(ui->btnCancel, m_Model, UIF_JOIN_MODE); + activateOnFlag(ui->btnCopySeg, m_Model, UIF_JOIN_MODE); + activateOnFlag(ui->btnClearSeg, m_Model, UIF_JOIN_MODE); + } + +void JoinDataPanel::on_btnStartCnJ_clicked(){ + + int sel = ui->inJoinType->currentIndex(); + switch(sel){ + case 0:{ + + IRISApplication *driver = m_Model->GetDriver(); + + //reset ROI from the main image + GlobalState::RegionType roi = + driver->GetCurrentImageData()->GetImageRegion(); + + // Can't be empty! + assert(roi.GetNumberOfPixels()); + + // Update + driver->GetGlobalState()->SetSegmentationROI(roi); + + //// Initialize the image data + driver->InitializeJOINImageData( + driver->GetGlobalState()->GetSegmentationROISettings(), sel, + NULL, + m_Model->GetProgressCommand()); + + driver->CopySegementationToJsrc( + driver->GetGlobalState()->GetSegmentationROISettings(), + m_Model->GetProgressCommand()); + driver->SetCurrentImageDataToJOIN(); + + // set tiled layout to ease understanding the interaction mode + driver->GetJOINImageData()->SetJdstSticky(false); + //m_Model->SetJsrcVisibility(true);//crashes, seems to be default anyway + //m_Model->SetJdstVisibility(true);//crashes, seems to be default anyway + m_Model->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_STACKED);//only when coming from stacked view will the tiled view get reorganized + m_Model->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_TILED); + m_Model->SetSegmentationVisibility(true); + } break; + case 1:{ + ////panel will be hidden in GWSJOIN_MODE + m_Model->GetGlobalState()->SetToolbarMode(GLOBALWS_ROI_MODE); + //ui->stack->setCurrentWidget(ui->pageSnakeTool); + //ui->grpInspector->setTitle("GWS ROI Inspector"); + } break; + case 2:{ + ////todo: implement use of ITK-Snap file-chooser for setting file name + + IRISApplication *driver = m_Model->GetDriver(); + + //reset ROI from the main image + GlobalState::RegionType roi = + driver->GetCurrentImageData()->GetImageRegion(); + + // Can't be empty! + assert(roi.GetNumberOfPixels()); + + // Update + driver->GetGlobalState()->SetSegmentationROI(roi); + + //// Initialize the image data + driver->InitializeJOINImageData( + driver->GetGlobalState()->GetSegmentationROISettings(), sel, + ui->JsrcInputFileName->text().toUtf8().constData(), + m_Model->GetProgressCommand()); + + driver->SetCurrentImageDataToJOIN(); + + // set tiled layout to ease understanding the interaction mode + driver->GetJOINImageData()->SetJdstSticky(false); + //m_Model->SetJsrcVisibility(true);//crashes, seems to be default anyway + //m_Model->SetJdstVisibility(true);//crashes, seems to be default anyway + m_Model->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_STACKED);//only when coming from stacked view will the tiled view get reorganized + m_Model->GetDisplayLayoutModel()->GetSliceViewLayerLayoutModel()->SetValue(LAYOUT_TILED); + m_Model->SetSegmentationVisibility(true); + } break; + default: + ////Switch to crosshairs mode + m_Model->GetGlobalState()->SetToolbarMode(CROSSHAIRS_MODE); + std::cerr << "Index not handled (yet)." << std::endl; + break; + }//switch + } + +void JoinDataPanel::on_btnFinishCnJ_clicked(){ + IRISApplication *driver = m_Model->GetDriver(); + GlobalState *gs = driver->GetGlobalState(); + + // Update IRIS with JOIN images + driver->UpdateIRISWithJOINImageData(NULL); + + // Set an undo point + driver->StoreUndoPoint("Automatic Segmentation"); + + // Return to IRIS mode + driver->SetCurrentImageDataToIRIS(); + driver->ReleaseJOINImageData(); + + ui->btnStartCnJ->setEnabled(true); + ui->btnCancel->setEnabled(false); + ui->btnFinishCnJ->setEnabled(false); + ui->inJoinType->setEnabled(true); + m_Model->GetGlobalState()->SetToolbarMode(CROSSHAIRS_MODE); //disables JoinInteraction + m_Model->SetSegmentationVisibility(true); + } + +void JoinDataPanel::on_btnCancel_clicked(){ + IRISApplication *driver = m_Model->GetDriver(); + GlobalState *gs = driver->GetGlobalState(); + + // Return to IRIS mode + driver->SetCurrentImageDataToIRIS(); + driver->ReleaseJOINImageData(); + + ui->btnStartCnJ->setEnabled(true); + ui->btnCancel->setEnabled(false); + ui->btnFinishCnJ->setEnabled(false); + ui->inJoinType->setEnabled(true); + m_Model->GetGlobalState()->SetToolbarMode(CROSSHAIRS_MODE); //disables JoinInteraction + m_Model->SetSegmentationVisibility(true); + } + + +void JoinDataPanel::on_btnCopySeg_clicked(){ + IRISApplication *driver = m_Model->GetDriver(); + driver->CopySegementationToJdst( + driver->GetGlobalState()->GetSegmentationROISettings(), + m_Model->GetProgressCommand()); + + driver->InvokeEvent(SegmentationChangeEvent()); + m_Model->SetSegmentationVisibility(true); + } + +void JoinDataPanel::on_btnClearSeg_clicked(){ + IRISApplication *driver = m_Model->GetDriver(); + driver->ClearJdst(); + + driver->InvokeEvent(SegmentationChangeEvent()); + m_Model->SetSegmentationVisibility(true); + } diff --git a/GUI/Qt/Components/JoinDataPanel.h b/GUI/Qt/Components/JoinDataPanel.h new file mode 100644 index 00000000..ee9a97a6 --- /dev/null +++ b/GUI/Qt/Components/JoinDataPanel.h @@ -0,0 +1,45 @@ +/* +part of Click'n'Join mode, which was contributed by Roman Grothausmann +*/ + +#ifndef JOINDATAPANEL_H +#define JOINDATAPANEL_H + +#include + +class GlobalUIModel; + +namespace Ui { +class JoinDataPanel; +} + +class JoinDataPanel : public SNAPComponent +{ + Q_OBJECT + +public: + explicit JoinDataPanel(QWidget *parent = 0); + ~JoinDataPanel(); + + void SetModel(GlobalUIModel *); + +private slots: + + void on_btnStartCnJ_clicked(); + + void on_btnCancel_clicked(); + + void on_btnFinishCnJ_clicked(); + + void on_btnCopySeg_clicked(); + + void on_btnClearSeg_clicked(); + +private: + Ui::JoinDataPanel *ui; + + GlobalUIModel *m_Model; +}; + + +#endif // JOINDATAPANEL_H diff --git a/GUI/Qt/Components/JoinDataPanel.ui b/GUI/Qt/Components/JoinDataPanel.ui new file mode 100644 index 00000000..488c63ee --- /dev/null +++ b/GUI/Qt/Components/JoinDataPanel.ui @@ -0,0 +1,285 @@ + + + JoinDataPanel + + + + 0 + 0 + 200 + 680 + + + + + 0 + 0 + + + + + 200 + 16777215 + + + + Form + + + * { +font-size: 12px; +} +QGroupBox { + background-origin: content; + margin-top: 15px; + font-weight: bold; + font-size: 12px; + font-family: helvetica; + color: rgb(30,30,160); + background-color: rgb(237,237,237); + padding: 5px; + border-radius: 4px; + border: 1px solid rgb(130,130,130); +} +QGroupBox::title { + subcontrol-origin: margin; + subcontrol-position: top left; +} + + + + + + 8 + + + 5 + + + + + + 0 + 0 + + + + Dataset: + + + + 0 + + + + + + 0 + 0 + + + + 0 + + + + + 0 + + + + + + 12 + + + 0 + + + + + Please choose the label data for Click 'n' Join: + + + Qt::PlainText + + + true + + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + Current Segmentation + + + + + Global Watershed (GWS) + + + + + Load from File + + + + + + + + + + + + 4 + + + 0 + + + + + false + + + + 0 + 0 + + + + + 64 + 16777215 + + + + Cancel + + + + + + + + 0 + 0 + + + + + 64 + 16777215 + + + + OK + + + + + + + + + + + + + + + + + false + + + + 0 + 0 + + + + Click LMB to add labeled regions to the "Join destination" with the current FG label, RMB to remove. +CTRL+LMB/RMB picks the FG/BG label from the "Join destination". +"Finish" will copy the final segmentation according to the chosen draw-over mode (BG label). + + + Qt::PlainText + + + true + + + + + + + + + + false + + + Copy Segmentation + + + + + + + false + + + Clear Segmentation + + + + + + + + + + false + + + Finish Click 'n' Join + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/GUI/Qt/Components/PaintbrushToolPanel.cxx b/GUI/Qt/Components/PaintbrushToolPanel.cxx index 626d56c7..1c9eab36 100644 --- a/GUI/Qt/Components/PaintbrushToolPanel.cxx +++ b/GUI/Qt/Components/PaintbrushToolPanel.cxx @@ -73,6 +73,7 @@ void PaintbrushToolPanel::SetModel(PaintbrushSettingsModel *model) makeCoupling(ui->inGranularity, model->GetThresholdLevelModel()); makeCoupling(ui->inSmoothness, model->GetSmoothingIterationsModel()); + makeCoupling(ui->chkDirect, model->GetDirecWSModel()); } void PaintbrushToolPanel::on_actionBrushStyle_triggered() diff --git a/GUI/Qt/Components/PaintbrushToolPanel.ui b/GUI/Qt/Components/PaintbrushToolPanel.ui index d6701623..3921c483 100644 --- a/GUI/Qt/Components/PaintbrushToolPanel.ui +++ b/GUI/Qt/Components/PaintbrushToolPanel.ui @@ -380,7 +380,7 @@ font-size:11px; 0 - + Granularity: @@ -390,14 +390,14 @@ font-size:11px; - + <html><head/><body><p><span style=" font-weight:600;">Adaptive brush granularity (^+,^-)</span></p><p>Lower values of this parameter lead to oversegmentation, while higher values lead to undersegmentation.</p></body></html> - + Smoothness: @@ -407,13 +407,20 @@ font-size:11px; - + <html><head/><body><p><span style=" font-weight:600;">Adaptive brush smoothness (⌥+,⌥-)</span></p><p>Larger values of this parameter produce smoother segments.</p></body></html> + + + + direct + + + diff --git a/GUI/Qt/Components/SliceViewPanel.cxx b/GUI/Qt/Components/SliceViewPanel.cxx index 96631fad..a06653c4 100644 --- a/GUI/Qt/Components/SliceViewPanel.cxx +++ b/GUI/Qt/Components/SliceViewPanel.cxx @@ -12,6 +12,7 @@ #include "PaintbrushRenderer.h" #include "SnakeROIRenderer.h" #include "SnakeROIModel.h" +#include "JoinModel.h" #include "SliceWindowCoordinator.h" #include "PolygonDrawingModel.h" #include "AnnotationModel.h" @@ -19,6 +20,7 @@ #include "QtWidgetActivator.h" #include "SnakeModeRenderer.h" #include "SnakeWizardModel.h" +#include "GlobalWSWizardModel.h" #include "DisplayLayoutModel.h" #include "PaintbrushModel.h" #include "SliceWindowDecorationRenderer.h" @@ -186,6 +188,7 @@ void SliceViewPanel::Initialize(GlobalUIModel *model, unsigned int index) ui->imThumbnail->SetModel(m_GlobalUI->GetCursorNavigationModel(index)); ui->imPolygon->SetModel(m_GlobalUI->GetPolygonDrawingModel(index)); ui->imSnakeROI->SetModel(m_GlobalUI->GetSnakeROIModel(index)); + ui->imJoin->SetModel(m_GlobalUI->GetJoinModel(index)); ui->imPaintbrush->SetModel(m_GlobalUI->GetPaintbrushModel(index)); ui->imAnnotation->SetModel(m_GlobalUI->GetAnnotationModel(index)); @@ -217,6 +220,10 @@ void SliceViewPanel::Initialize(GlobalUIModel *model, unsigned int index) connectITK(m_GlobalUI->GetSnakeROIModel(index), ModelUpdateEvent()); + // Listen to the join model too + connectITK(m_GlobalUI->GetJoinModel(index), + ModelUpdateEvent()); + // Listen to paintbrush motion connectITK(m_GlobalUI->GetPaintbrushModel(index), PaintbrushModel::PaintbrushMovedEvent()); @@ -227,6 +234,9 @@ void SliceViewPanel::Initialize(GlobalUIModel *model, unsigned int index) // Listen to all (?) events from the snake wizard as well connectITK(m_GlobalUI->GetSnakeWizardModel(), IRISEvent()); + // Listen to all (?) events from the GlobalWS wizard as well + connectITK(m_GlobalUI->GetGlobalWSWizardModel(), IRISEvent()); + // Widget coupling makeCoupling(ui->inSlicePosition, m_SliceModel->GetSliceIndexModel()); @@ -400,7 +410,12 @@ void SliceViewPanel::OnToolbarModeChange() ConfigureEventChain(ui->imAnnotation); ovTiled.push_back(ui->imAnnotation->GetRenderer()); break; - case ROI_MODE: + case JOIN_MODE: + case GWSJOIN_MODE: + ConfigureEventChain(ui->imJoin); + break; + case GLOBALWS_ROI_MODE: + case SNAKE_ROI_MODE: ConfigureEventChain(ui->imSnakeROI); ovTiled.push_back(ui->imSnakeROI->GetRenderer()); break; diff --git a/GUI/Qt/Components/SliceViewPanel.ui b/GUI/Qt/Components/SliceViewPanel.ui index 17c338bc..db55a2bc 100644 --- a/GUI/Qt/Components/SliceViewPanel.ui +++ b/GUI/Qt/Components/SliceViewPanel.ui @@ -832,6 +832,9 @@ p, li { white-space: pre-wrap; } + + + @@ -1185,6 +1188,12 @@ p, li { white-space: pre-wrap; }
PaintbrushInteractionMode.h
1 + + JoinInteractionMode + QWidget +
JoinInteractionMode.h
+ 1 +
AnnotationInteractionMode QWidget diff --git a/GUI/Qt/Components/SnakeToolROIPanel.cxx b/GUI/Qt/Components/SnakeToolROIPanel.cxx index af5856e6..8bd88a47 100644 --- a/GUI/Qt/Components/SnakeToolROIPanel.cxx +++ b/GUI/Qt/Components/SnakeToolROIPanel.cxx @@ -58,7 +58,8 @@ void SnakeToolROIPanel::on_btnAuto_clicked() // TODO: Check that the label configuration is valid // Handle resampling if requested - if(ui->chkResample->isChecked()) + if(ui->chkResample->isChecked() + && m_Model->GetGlobalState()->GetToolbarMode() != GLOBALWS_ROI_MODE) //hack to not resample for GWS (better would be to disable chkResample { if(m_ResampleDialog->exec() != QDialog::Accepted) return; @@ -70,10 +71,19 @@ void SnakeToolROIPanel::on_btnAuto_clicked() m_Model->GetSnakeROIResampleModel()->Accept(); } - // Switch to crosshairs mode - m_Model->GetGlobalState()->SetToolbarMode(CROSSHAIRS_MODE); - // Show the snake panel MainImageWindow *main = findParentWidget(this); + + switch(m_Model->GetGlobalState()->GetToolbarMode()){ + case SNAKE_ROI_MODE: main->OpenSnakeWizard(); + break; + case GLOBALWS_ROI_MODE: + main->OpenGlobalWSWizard(); + break; + default: + // Switch to crosshairs mode + m_Model->GetGlobalState()->SetToolbarMode(CROSSHAIRS_MODE); + break; + } } diff --git a/GUI/Qt/Resources/SNAPResources.qrc b/GUI/Qt/Resources/SNAPResources.qrc index f52d78c7..2f4f496d 100644 --- a/GUI/Qt/Resources/SNAPResources.qrc +++ b/GUI/Qt/Resources/SNAPResources.qrc @@ -5,6 +5,8 @@ zoom.gif spray.gif snake.gif + globalWS.gif + roi.gif screencapture2.gif screencapture.gif scalpel.gif diff --git a/GUI/Qt/Resources/globalWS.gif b/GUI/Qt/Resources/globalWS.gif new file mode 100644 index 00000000..48e95eeb Binary files /dev/null and b/GUI/Qt/Resources/globalWS.gif differ diff --git a/GUI/Qt/Resources/roi.gif b/GUI/Qt/Resources/roi.gif new file mode 100644 index 00000000..369970e4 Binary files /dev/null and b/GUI/Qt/Resources/roi.gif differ diff --git a/GUI/Qt/View/JoinInteractionMode.cxx b/GUI/Qt/View/JoinInteractionMode.cxx new file mode 100644 index 00000000..59d6122e --- /dev/null +++ b/GUI/Qt/View/JoinInteractionMode.cxx @@ -0,0 +1,50 @@ +#include "JoinInteractionMode.h" +#include "SliceViewPanel.h" +#include "GlobalUIModel.h" + +JoinInteractionMode::JoinInteractionMode(GenericSliceView *parent) + : SliceWindowInteractionDelegateWidget(parent){ + m_Model = NULL; + } + +JoinInteractionMode::~JoinInteractionMode(){ + } + +void +JoinInteractionMode +::SetModel(JoinModel *model){ + m_Model = model; + SetParentModel(model->GetParent()); + } + +void JoinInteractionMode::mousePressEvent(QMouseEvent *ev){ + + if(m_Model->GetParent()->GetParentUI()->CheckState(UIF_JOIN_MODE)){ + bool isleft = (ev->button() == Qt::LeftButton); + bool isright = (ev->button() == Qt::RightButton); + + Qt::KeyboardModifiers modifiers = ev->modifiers(); + bool ctrl= modifiers.testFlag( Qt::ControlModifier); + + if(isleft || isright) + { + if(m_Model->ProcessPushEvent(to_float(m_XSlice),isright,ctrl))//ProcessPushEvent currently always returns false + ev->accept(); //eat event, i.e. no cursor chasing + } + } + } + +void JoinInteractionMode::enterEvent(QEvent *){ + // TODO: this is hideous! + SliceViewPanel *panel = dynamic_cast(m_ParentView->parent()); + panel->SetMouseMotionTracking(true); + } + +void JoinInteractionMode::leaveEvent(QEvent *){ + SliceViewPanel *panel = dynamic_cast(m_ParentView->parent()); + panel->SetMouseMotionTracking(false); + + // This fixes a crash when you press quit in paintbrush mode + if(panel->isVisible()) + m_Model->ProcessLeaveEvent(); + } diff --git a/GUI/Qt/View/JoinInteractionMode.h b/GUI/Qt/View/JoinInteractionMode.h new file mode 100644 index 00000000..9268cfc9 --- /dev/null +++ b/GUI/Qt/View/JoinInteractionMode.h @@ -0,0 +1,34 @@ +#ifndef JOININTERACTIONMODE_H +#define JOININTERACTIONMODE_H + +#include +#include +#include "GenericSliceView.h" +#include "JoinModel.h" + +class GenericSliceModel; +class GenericSliceView; +class JoinModel; + +class JoinInteractionMode : public SliceWindowInteractionDelegateWidget +{ + Q_OBJECT + +public: + JoinInteractionMode(GenericSliceView *parent = 0); + ~JoinInteractionMode(); + + irisGetMacro(Model, JoinModel *) + void SetModel(JoinModel *m_Model); + + void mousePressEvent(QMouseEvent *ev); + + void enterEvent(QEvent *); + void leaveEvent(QEvent *); + +protected: + + JoinModel *m_Model; +}; + +#endif // JOININTERACTIONMODE_H diff --git a/GUI/Qt/View/PaintbrushInteractionMode.cxx b/GUI/Qt/View/PaintbrushInteractionMode.cxx index 425aaf49..f4562e84 100644 --- a/GUI/Qt/View/PaintbrushInteractionMode.cxx +++ b/GUI/Qt/View/PaintbrushInteractionMode.cxx @@ -27,6 +27,20 @@ ::SetModel(PaintbrushModel *model) SetParentModel(model->GetParent()); } +void PaintbrushInteractionMode::wheelEvent(QWheelEvent *ev) + { + ////catch wheel event if CTRL is pressed, otherwise pass it to slice scroll + Qt::KeyboardModifiers modifiers = ev->modifiers(); + if(modifiers.testFlag( Qt::ControlModifier )){ + ///accept event even if no voxel changed (i.e. not every level causes a WS change!) + //if(m_Model->ProcessWheelEvent(ev->delta())){ + m_Model->ProcessWheelEvent(ev->delta()); + ////do not pass event to cursor chasing + ev->accept(); + //} + } + } + void PaintbrushInteractionMode::mousePressEvent(QMouseEvent *ev) { bool isleft = (ev->button() == Qt::LeftButton); @@ -73,6 +87,7 @@ void PaintbrushInteractionMode::enterEvent(QEvent *) // TODO: this is hideous! SliceViewPanel *panel = dynamic_cast(m_ParentView->parent()); panel->SetMouseMotionTracking(true); + //m_Model->ProcessMouseEnterEvent(); } void PaintbrushInteractionMode::leaveEvent(QEvent *) diff --git a/GUI/Qt/View/PaintbrushInteractionMode.h b/GUI/Qt/View/PaintbrushInteractionMode.h index 04a9d7d3..c3aeca39 100644 --- a/GUI/Qt/View/PaintbrushInteractionMode.h +++ b/GUI/Qt/View/PaintbrushInteractionMode.h @@ -3,6 +3,7 @@ #include #include +#include class GenericSliceModel; class GenericSliceView; @@ -23,6 +24,7 @@ class PaintbrushInteractionMode : public SliceWindowInteractionDelegateWidget irisGetMacro(Renderer, PaintbrushRenderer *) + void wheelEvent(QWheelEvent *ev); void mousePressEvent(QMouseEvent *ev); void mouseMoveEvent(QMouseEvent *ev); void mouseReleaseEvent(QMouseEvent *ev); diff --git a/GUI/Qt/Windows/LayerInspectorDialog.cxx b/GUI/Qt/Windows/LayerInspectorDialog.cxx index 5338cf0a..407ca9e9 100644 --- a/GUI/Qt/Windows/LayerInspectorDialog.cxx +++ b/GUI/Qt/Windows/LayerInspectorDialog.cxx @@ -168,6 +168,7 @@ void LayerInspectorDialog::GenerateModelsForLayers() LayerIterator it = m_Model->GetDriver()->GetCurrentImageData()->GetLayers( MAIN_ROLE | OVERLAY_ROLE | + JOIN_ROLE | SNAP_ROLE); for(; !it.IsAtEnd(); ++it) @@ -196,6 +197,7 @@ void LayerInspectorDialog::BuildLayerWidgetHierarchy() mapRoleNames[MAIN_ROLE] = "Main Image"; mapRoleNames[OVERLAY_ROLE] = "Additional Images"; mapRoleNames[SNAP_ROLE] = "Snake Mode Layers"; + mapRoleNames[JOIN_ROLE] = "Join Mode Layers"; } // Get the top-level layout in the pane @@ -231,7 +233,7 @@ void LayerInspectorDialog::BuildLayerWidgetHierarchy() // Iterate over the layers for each class of displayed layers LayerIterator it = m_Model->GetDriver()->GetCurrentImageData()->GetLayers( - MAIN_ROLE | OVERLAY_ROLE | SNAP_ROLE); + MAIN_ROLE | OVERLAY_ROLE | SNAP_ROLE | JOIN_ROLE); // The current role and associated group box LayerRole currentRole = NO_ROLE; diff --git a/GUI/Qt/Windows/MainControlPanel.cxx b/GUI/Qt/Windows/MainControlPanel.cxx index 01393018..02fb3dbd 100644 --- a/GUI/Qt/Windows/MainControlPanel.cxx +++ b/GUI/Qt/Windows/MainControlPanel.cxx @@ -93,6 +93,7 @@ MainControlPanel::MainControlPanel(MainImageWindow *parent) : ui->btnZoom->setDefaultAction(FindUpstreamAction(this, "actionZoomPan")); ui->btnPolygon->setDefaultAction(FindUpstreamAction(this, "actionPolygon")); ui->btnPaintbrush->setDefaultAction(FindUpstreamAction(this, "actionPaintbrush")); + ui->btnJoin->setDefaultAction(FindUpstreamAction(this, "actionJoin")); ui->btnAnnotation->setDefaultAction(FindUpstreamAction(this, "actionAnnotation")); ui->btnSnake->setDefaultAction(FindUpstreamAction(this, "actionSnake")); @@ -146,6 +147,7 @@ MainControlPanel::MainControlPanel(MainImageWindow *parent) : ui->btnPaintbrushInspector->setVisible(false); ui->btnPolygonInspector->setVisible(false); ui->btnSnakeInspector->setVisible(false); + ui->btnJoinInspector->setVisible(false); ui->btnAnnotateInspector->setVisible(false); @@ -191,6 +193,7 @@ void MainControlPanel::SetModel(GlobalUIModel *model) ui->pagePaintbrushTool->SetModel(m_Model->GetPaintbrushSettingsModel()); ui->pageSnakeTool->SetModel(m_Model); ui->pagePolygonTool->SetModel(m_Model); + ui->pageJoinTool->SetModel(m_Model); ui->pageAnnotationTool->SetModel(m_Model); ui->btnLabelSelector->SetModel(model); @@ -209,12 +212,17 @@ void MainControlPanel::SetModel(GlobalUIModel *model) void MainControlPanel::onModelUpdate(const EventBucket &bucket) { // A static array of widget/mode mappings + //// mode_inspector_btn, mode_tool_pages and ToolbarModeType should have the same amount of entries! static QToolButton *mode_inspector_btn[] = { - ui->btnCursorInspector, - ui->btnZoomInspector, - ui->btnPolygonInspector, - ui->btnPaintbrushInspector, - ui->btnSnakeInspector, + ui->btnCursorInspector, //0: CROSSHAIRS_MODE + ui->btnZoomInspector, //1: NAVIGATION_MODE + ui->btnPolygonInspector, //2: POLYGON_DRAWING_MODE + ui->btnPaintbrushInspector, //3: PAINTBRUSH_MODE + ui->btnSnakeInspector, //4: SNAKE_ROI_MODE + ui->btnJoinInspector, //5: JOIN_MODE + ui->btnSnakeInspector, //6: GLOBALWS_ROI_MODE + ui->btnCursorInspector, //7: GWSJOIN_MODE + ui->btnSnakeInspector, //8: ANNOTATION_MODE ui->btnAnnotateInspector }; @@ -227,7 +235,8 @@ void MainControlPanel::onModelUpdate(const EventBucket &bucket) ToolbarModeType mode = gs->GetToolbarMode(); ui->btnPaintbrushInspector->setVisible(mode == PAINTBRUSH_MODE); ui->btnPolygonInspector->setVisible(mode == POLYGON_DRAWING_MODE); - ui->btnSnakeInspector->setVisible(mode == ROI_MODE); + ui->btnSnakeInspector->setVisible(mode == SNAKE_ROI_MODE || mode == GLOBALWS_ROI_MODE); + ui->btnJoinInspector->setVisible(mode == JOIN_MODE); ui->btnAnnotateInspector->setVisible(mode == ANNOTATION_MODE); // Click the button corresponding to the mode @@ -316,6 +325,14 @@ void MainControlPanel::on_btnSnakeInspector_clicked(bool checked) } +void MainControlPanel::on_btnJoinInspector_clicked(bool checked) +{ + if(checked) + { + ui->stack->setCurrentWidget(ui->pageJoinTool); + ui->grpInspector->setTitle("Join Inspector"); + } +} void MainControlPanel::on_btnAnnotateInspector_clicked(bool checked) { diff --git a/GUI/Qt/Windows/MainControlPanel.h b/GUI/Qt/Windows/MainControlPanel.h index e57408bc..5a4b87a0 100644 --- a/GUI/Qt/Windows/MainControlPanel.h +++ b/GUI/Qt/Windows/MainControlPanel.h @@ -47,6 +47,8 @@ private slots: void on_btnSnakeInspector_clicked(bool checked); + void on_btnJoinInspector_clicked(bool checked); + void on_btnAnnotateInspector_clicked(bool checked); private: diff --git a/GUI/Qt/Windows/MainControlPanel.ui b/GUI/Qt/Windows/MainControlPanel.ui index f4d455e4..31633013 100644 --- a/GUI/Qt/Windows/MainControlPanel.ui +++ b/GUI/Qt/Windows/MainControlPanel.ui @@ -6,19 +6,19 @@ 0 0 - 184 + 191 715 - 184 + 191 0 - 184 + 191 16777215 @@ -297,6 +297,20 @@ QToolButton:checked {
+ + + + ... + + + + :/root/globalWS.gif:/root/globalWS.gif + + + true + + + @@ -774,6 +788,35 @@ QWidget#widget { + + + + + 25 + 27 + + + + <html><head/><body><p><span style=" font-weight:600;">Join Inspector</span></p><p>Choose mode for Join tool.</p></body></html> + + + + + + ... + + + + :/root/globalWS.gif:/root/globalWS.gif + + + true + + + true + + + @@ -837,6 +880,7 @@ QWidget#widget { + @@ -1070,6 +1114,12 @@ QToolButton:checked {
SynchronizationInspector.h
1 + + JoinDataPanel + QWidget +
JoinDataPanel.h
+ 1 +
PolygonToolPanel QWidget diff --git a/GUI/Qt/Windows/MainImageWindow.cxx b/GUI/Qt/Windows/MainImageWindow.cxx index 267efdf2..7d3aa3eb 100644 --- a/GUI/Qt/Windows/MainImageWindow.cxx +++ b/GUI/Qt/Windows/MainImageWindow.cxx @@ -39,6 +39,7 @@ #include "ColorMapModel.h" #include "ViewPanel3D.h" #include "SnakeWizardPanel.h" +#include "GlobalWSWizardPanel.h" #include "LatentITKEventNotifier.h" #include #include "QtReporterDelegates.h" @@ -179,6 +180,7 @@ MainImageWindow::MainImageWindow(QWidget *parent) : ui->actionPolygon->setActionGroup(grpToolbarMain); ui->actionPaintbrush->setActionGroup(grpToolbarMain); ui->actionSnake->setActionGroup(grpToolbarMain); + ui->actionJoin->setActionGroup(grpToolbarMain); ui->actionAnnotation->setActionGroup(grpToolbarMain); QActionGroup *grpToolbar3D = new QActionGroup(this); @@ -233,6 +235,15 @@ MainImageWindow::MainImageWindow(QWidget *parent) : QDockWidget::DockWidgetMovable); this->addDockWidget(Qt::RightDockWidgetArea, m_DockRight); + m_DockRight2 = new QDockWidget("Global Watershed", this); + m_GlobalWSWizard = new GlobalWSWizardPanel(this); + m_DockRight2->setWidget(m_GlobalWSWizard); + m_DockRight2->setAllowedAreas(Qt::RightDockWidgetArea); + m_DockRight2->setFeatures( + QDockWidget::DockWidgetFloatable | + QDockWidget::DockWidgetMovable); + this->addDockWidget(Qt::RightDockWidgetArea, m_DockRight2); + // Set up the recent items panels connect(ui->panelRecentImages, SIGNAL(RecentItemSelected(QString)), SLOT(LoadMainImage(QString))); @@ -252,11 +263,16 @@ MainImageWindow::MainImageWindow(QWidget *parent) : // Hide the right dock for now m_DockRight->setVisible(false); + m_DockRight2->setVisible(false); // Hide the dock when the wizard finishes connect(m_SnakeWizard, SIGNAL(wizardFinished()), this, SLOT(onSnakeWizardFinished())); + // Hide the dock when the wizard finishes + connect(m_GlobalWSWizard, SIGNAL(wizardFinished()), + this, SLOT(onGlobalWSWizardFinished())); + // Make the margins adjust when the docks are attached connect(m_DockLeft, SIGNAL(dockLocationChanged(Qt::DockWidgetArea)), this, SLOT(AdjustMarginsForDocks())); @@ -445,6 +461,7 @@ void MainImageWindow::Initialize(GlobalUIModel *model) m_LabelEditor->SetModel(model->GetLabelEditorModel()); m_LayerInspector->SetModel(model); m_SnakeWizard->SetModel(model); + m_GlobalWSWizard->SetModel(model); m_ReorientImageDialog->SetModel(model->GetReorientImageModel()); m_DropDialog->SetModel(model); m_StatisticsDialog->SetModel(model); @@ -513,12 +530,12 @@ void MainImageWindow::Initialize(GlobalUIModel *model) // Set up activations - File menu activateOnFlag(ui->actionOpenMain, m_Model, UIF_IRIS_MODE); activateOnFlag(ui->menuRecent_Images, m_Model, UIF_IRIS_MODE); - activateOnFlag(ui->actionSaveMain, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); + activateOnFlag(ui->actionSaveMain, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE);//CnJ: Main(ROI) can be saved with RMB on layer in cursor inspector activateOnFlag(ui->actionSaveSpeed, m_Model, UIF_SNAKE_MODE); activateOnFlag(ui->actionSaveLevelSet, m_Model, UIF_LEVEL_SET_ACTIVE); activateOnFlag(ui->actionSaveMainROI, m_Model, UIF_SNAKE_MODE); activateOnFlag(ui->menuExport, m_Model, UIF_BASEIMG_LOADED); - activateOnFlag(ui->actionUnload_All, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); + activateOnFlag(ui->actionUnload_All, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); // Set up activations - Edit menu activateOnFlag(ui->actionUndo, m_Model, UIF_UNDO_POSSIBLE); @@ -541,9 +558,9 @@ void MainImageWindow::Initialize(GlobalUIModel *model) // Set up activations - Segmentation menu - activateOnFlag(ui->actionLoad_from_Image, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); - activateOnFlag(ui->actionClear, m_Model, UIF_BASEIMG_LOADED); - activateOnFlag(ui->actionSaveSegmentation, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); + activateOnFlag(ui->actionLoad_from_Image, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + activateOnAllFlags(ui->actionClear, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE, UIF_BASEIMG_LOADED); + activateOnFlag(ui->actionSaveSegmentation, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); activateOnFlag(ui->actionSaveSegmentationAs, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); activateOnFlag(ui->actionSave_as_Mesh, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); activateOnFlag(ui->actionLoadLabels, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); @@ -552,30 +569,32 @@ void MainImageWindow::Initialize(GlobalUIModel *model) activateOnFlag(ui->menuAppearance, m_Model, UIF_BASEIMG_LOADED); // Overlay action activations - activateOnFlag(ui->actionAdd_Overlay, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); - // activateOnAllFlags(ui->actionUnload_Last_Overlay, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED, UIF_OVERLAY_LOADED); - activateOnAllFlags(ui->actionUnload_All_Overlays, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED, UIF_OVERLAY_LOADED); + activateOnFlag(ui->actionAdd_Overlay, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + // activateOnAllFlags(ui->actionUnload_Last_Overlay, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE, UIF_OVERLAY_LOADED); + activateOnAllFlags(ui->actionUnload_All_Overlays, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE, UIF_OVERLAY_LOADED); + activateOnFlag(ui->actionToggleJsrcVis, m_Model, UIF_JOIN_MODE); activateOnFlag(ui->actionOverlayVisibilityToggleAll, m_Model, UIF_OVERLAY_LOADED); activateOnFlag(ui->actionOverlayVisibilityDecreaseAll, m_Model, UIF_OVERLAY_LOADED); activateOnFlag(ui->actionOverlayVisibilityIncreaseAll, m_Model, UIF_OVERLAY_LOADED); // Workspace menu activateOnFlag(ui->actionOpenWorkspace, m_Model, UIF_IRIS_MODE); - activateOnFlag(ui->actionSaveWorkspace, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); - activateOnFlag(ui->actionSaveWorkspaceAs, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); + activateOnFlag(ui->actionSaveWorkspace, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + activateOnFlag(ui->actionSaveWorkspaceAs, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); // Tool action activations - activateOnFlag(ui->actionCrosshair, m_Model, UIF_BASEIMG_LOADED); - activateOnFlag(ui->actionZoomPan, m_Model, UIF_BASEIMG_LOADED); - activateOnFlag(ui->actionPolygon, m_Model, UIF_BASEIMG_LOADED); - activateOnFlag(ui->actionSnake, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); - activateOnFlag(ui->actionPaintbrush, m_Model, UIF_BASEIMG_LOADED); - activateOnFlag(ui->actionAnnotation, m_Model, UIF_BASEIMG_LOADED); + activateOnFlag(ui->actionCrosshair, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + activateOnFlag(ui->actionZoomPan, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + activateOnFlag(ui->actionPolygon, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + activateOnFlag(ui->actionSnake, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + activateOnFlag(ui->actionPaintbrush, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + activateOnFlag(ui->actionJoin, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + activateOnFlag(ui->actionAnnotation, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); activateOnFlag(ui->action3DCrosshair, m_Model, UIF_BASEIMG_LOADED); activateOnFlag(ui->action3DTrackball, m_Model, UIF_BASEIMG_LOADED); - activateOnFlag(ui->action3DScalpel, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); - activateOnFlag(ui->action3DSpray, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); + activateOnFlag(ui->action3DScalpel, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); + activateOnFlag(ui->action3DSpray, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); activateOnFlag(ui->actionLayerInspector, m_Model, UIF_BASEIMG_LOADED); activateOnFlag(ui->actionImage_Contrast, m_Model, UIF_BASEIMG_LOADED); @@ -586,7 +605,7 @@ void MainImageWindow::Initialize(GlobalUIModel *model) activateOnFlag(ui->actionImage_Information, m_Model, UIF_BASEIMG_LOADED); activateOnFlag(ui->actionLabel_Editor, m_Model, UIF_BASEIMG_LOADED); - activateOnFlag(ui->actionReorient_Image, m_Model, UIF_IRIS_WITH_BASEIMG_LOADED); + activateOnFlag(ui->actionReorient_Image, m_Model, UIF_NOT_SNAKE_OR_JOIN_MODE); // Hook up toolbar actions to the toolbar makeActionGroupCoupling(this->GetMainToolActionGroup(), @@ -1048,6 +1067,18 @@ void MainImageWindow::OpenSnakeWizard() m_DockRight->setVisible(true); } +void MainImageWindow::OpenGlobalWSWizard() +{ + // Initialize the GlobalWS wizard + this->m_GlobalWSWizard->Initialize(); + + // Remember the size of the window before the right dock was shown + m_SizeWithoutRightDock = this->size(); + + // Make the dock containing the wizard visible + m_DockRight2->setVisible(true); +} + void MainImageWindow::AdjustMarginsForDocks() { // Get the current margins @@ -1318,6 +1349,20 @@ void MainImageWindow::onSnakeWizardFinished() this->UpdateCanvasDimensions(); } +void MainImageWindow::onGlobalWSWizardFinished() +{ + // Make the dock containing the wizard visible + m_DockRight2->setVisible(false); + + // TODO: this way of handling the size of the main window after the right + // dock is hidden is rudimentary. I should learn how to use sizePolicy and + // sizeHint fields more effectively. + + // Return to previous size + this->layout()->activate(); + resize(m_SizeWithoutRightDock.width(), m_SizeWithoutRightDock.height()); +} + void MainImageWindow::on_actionUnload_All_triggered() { // Prompt for unsaved changes @@ -2089,6 +2134,10 @@ void MainImageWindow::on_actionUnload_Last_Overlay_triggered() } } +void MainImageWindow::on_actionToggleJsrcVis_triggered() +{ + m_Model->ToggleJsrcVisibility(); +} void MainImageWindow::on_actionToggleLayerLayout_triggered() { diff --git a/GUI/Qt/Windows/MainImageWindow.h b/GUI/Qt/Windows/MainImageWindow.h index df7fa76b..1e1409c8 100644 --- a/GUI/Qt/Windows/MainImageWindow.h +++ b/GUI/Qt/Windows/MainImageWindow.h @@ -36,6 +36,7 @@ class SliceViewPanel; class GlobalUIModel; class QDockWidget; class SnakeWizardPanel; +class GlobalWSWizardPanel; class EventBucket; class QModelIndex; class QProgressDialog; @@ -85,6 +86,9 @@ class MainImageWindow : public QMainWindow // Initiate active contour segmentation void OpenSnakeWizard(); + // Initiate watershed segmentation + void OpenGlobalWSWizard(); + // Load a drag-n-dropped file void LoadDroppedFile(QString file); @@ -138,6 +142,8 @@ private slots: void onSnakeWizardFinished(); + void onGlobalWSWizardFinished(); + void on_actionUnload_All_triggered(); void on_actionReorient_Image_triggered(); @@ -273,6 +279,8 @@ private slots: void on_actionCoregister_Overlay_triggered(); + void on_actionToggleJsrcVis_triggered(); + void on_actionToggleLayerLayout_triggered(); void on_actionActivateNextLayer_triggered(); @@ -316,7 +324,7 @@ private slots: QWidget *m_ViewPanels[4]; // Left and right docks - QDockWidget *m_DockLeft, *m_DockRight; + QDockWidget *m_DockLeft, *m_DockRight, *m_DockRight2; // Size before the right dock is shown QSize m_SizeWithoutRightDock; @@ -332,6 +340,9 @@ private slots: // SNAP wizard panel (in right dock) SnakeWizardPanel *m_SnakeWizard; + // GlobalWS wizard panel (in right dock) + GlobalWSWizardPanel *m_GlobalWSWizard; + Ui::MainImageWindow *ui; GlobalUIModel *m_Model; diff --git a/GUI/Qt/Windows/MainImageWindow.ui b/GUI/Qt/Windows/MainImageWindow.ui index aaa3c5e8..71b5ba29 100644 --- a/GUI/Qt/Windows/MainImageWindow.ui +++ b/GUI/Qt/Windows/MainImageWindow.ui @@ -437,6 +437,7 @@ color:rgb(103, 103, 103); Appearance + @@ -472,6 +473,7 @@ color:rgb(103, 103, 103); +
@@ -1395,6 +1397,35 @@ color:rgb(103, 103, 103); Co-register an image with the main image currently loaded into ITK-SNAP + + + true + + + + :/root/globalWS.gif:/root/globalWS.gif + + + Click 'n' Join Mode + + + <html><head/><body><p><span style=" font-weight:600;">Click 'n' Join Mode</span></p><p>Join labeled regions by clicking.</p></body></html> + + + J + + + + + ToggleJsrcVis + + + Toggle Join source image visibility + + + T + + Recent 1 diff --git a/Logic/Common/SNAPRegistryIO.cxx b/Logic/Common/SNAPRegistryIO.cxx index 36eb4d0c..33b5f92b 100644 --- a/Logic/Common/SNAPRegistryIO.cxx +++ b/Logic/Common/SNAPRegistryIO.cxx @@ -454,6 +454,7 @@ void SNAPRegistryIO::BuildEnums() m_EnumMapLayerRole.AddPair(LABEL_ROLE, "SegmentationRole"); m_EnumMapLayerRole.AddPair(SNAP_ROLE, "SnakeModeRole"); m_EnumMapLayerRole.AddPair(NO_ROLE, "InvalidRole"); + m_EnumMapLayerRole.AddPair(JOIN_ROLE, "JoinModeRole"); m_EnumMapLayerLayout.AddPair(LAYOUT_STACKED, "Stacked"); m_EnumMapLayerLayout.AddPair(LAYOUT_TILED, "Tiled"); diff --git a/Logic/Common/itkJoinCopyFilter.cxx b/Logic/Common/itkJoinCopyFilter.cxx new file mode 100644 index 00000000..cf817ba3 --- /dev/null +++ b/Logic/Common/itkJoinCopyFilter.cxx @@ -0,0 +1,76 @@ +////image filter to set DrawingColor in output image if input pixel equals SeedValue + +#ifndef __itkJoinCopyFilter_cxx +#define __itkJoinCopyFilter_cxx + +#include "itkJoinCopyFilter.h" +#include +#include +#include + +namespace itk{ + + template + JoinCopyFilter + ::JoinCopyFilter(){ + m_SeedActive= false; + m_SeedIndex.Fill(0); + m_SeedValue= NumericTraits::Zero; + } + + template + void JoinCopyFilter + ::SetJsrc(const TInputImage1 *image1){ + this->SetNthInput( 0, const_cast< TInputImage1 * >( image1 ) ); + } + + template + void JoinCopyFilter + ::SetJdst(const TInputImage2 *image2){ + this->SetNthInput( 1, const_cast< TInputImage2 * >( image2 ) ); + } + + + template + void JoinCopyFilter + ::BeforeThreadedGenerateData(){ + + if(m_SeedActive){ + typename TInputImage1::ConstPointer input = dynamic_cast( ProcessObject::GetInput(0) ); + m_SeedValue= input->GetPixel(m_SeedIndex); + } + m_UpdateFlag= false; + } + + + template + void JoinCopyFilter + ::ThreadedGenerateData(const typename Superclass::OutputImageRegionType& outputRegionForThread, ThreadIdType threadId){ + + if(m_SeedActive){ + typename TInputImage1::ConstPointer input = dynamic_cast( ProcessObject::GetInput(0) ); + typename TInputImage2::Pointer output = this->GetOutput(); + + itk::ImageRegionConstIterator iti(input, outputRegionForThread); + itk::ImageRegionIterator ito(output, outputRegionForThread); + + // support progress methods/callbacks with 1000 updates + ProgressReporter progress(this, threadId, outputRegionForThread.GetNumberOfPixels(), 1000); + + while(!iti.IsAtEnd()){ + if(iti.Get() == m_SeedValue){ + ito.Set(m_DrawingColor); + } + ++iti; + ++ito; + progress.CompletedPixel(); + } + + m_UpdateFlag= true; + m_SeedActive= false; + //output->Modified(); + } + } + }// end namespace + +#endif //__itkJoinCopyFilter_cxx diff --git a/Logic/Common/itkJoinCopyFilter.h b/Logic/Common/itkJoinCopyFilter.h new file mode 100644 index 00000000..982897c8 --- /dev/null +++ b/Logic/Common/itkJoinCopyFilter.h @@ -0,0 +1,73 @@ +#ifndef __itkJoinCopyFilter_h +#define __itkJoinCopyFilter_h + +#include + +namespace itk{ + /** \class JoinCopyFilter + * \brief: sets DrawingColor in output image if input pixel equals SeedValue + * + * \ingroup ImageFilters + */ + template + class JoinCopyFilter: + public InPlaceImageFilter{ + public: + /** Standard class typedefs. */ + typedef JoinCopyFilter Self; + typedef InPlaceImageFilter Superclass; + typedef SmartPointer Pointer; + typedef SmartPointer< const Self > ConstPointer; + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(JoinCopyFilter, InPlaceImageFilter); + + void SetJsrc(const TInputImage1 *image1); + void SetJdst(const TInputImage2 *image2); + + /** Set/Get SeedIndex value */ + itkSetMacro(SeedIndex, typename TInputImage1::IndexType); + itkGetConstMacro(SeedIndex, typename TInputImage1::IndexType); + + /** Set/Get DrawingColor value */ + itkSetMacro(DrawingColor, typename TInputImage2::ValueType); + itkGetConstMacro(DrawingColor, typename TInputImage2::ValueType); + + /** Get UpdateFlag value */ + itkGetConstMacro(UpdateFlag, bool); + + /** Get/Set SeedActive value */ + itkSetMacro(SeedActive, bool); + itkGetConstMacro(SeedActive, bool); + + protected: + JoinCopyFilter(); + ~JoinCopyFilter(){} + + bool m_SeedActive; + typename TInputImage1::IndexType m_SeedIndex; + typename TInputImage1::ValueType m_SeedValue; + typename TInputImage2::ValueType m_DrawingColor; + bool m_UpdateFlag; + + + virtual void BeforeThreadedGenerateData(); + virtual void ThreadedGenerateData(const typename Superclass::OutputImageRegionType& outputRegionForThread, ThreadIdType threadId); + + private: + JoinCopyFilter(const Self &); //purposely not implemented + void operator=(const Self &); //purposely not implemented + + }; + } //namespace ITK + + +#ifndef ITK_MANUAL_INSTANTIATION +#include "itkJoinCopyFilter.cxx" +#endif + +#endif // __itkJoinCopyFilter_h + diff --git a/Logic/Framework/GenericImageData.h b/Logic/Framework/GenericImageData.h index 6a4b3d1d..7da9f4ec 100644 --- a/Logic/Framework/GenericImageData.h +++ b/Logic/Framework/GenericImageData.h @@ -348,6 +348,7 @@ class GenericImageData : public itk::Object SmartPtr m_Annotations; friend class SNAPImageData; + friend class JOINImageData; friend class LayerIterator; // Create a wrapper (vector or scalar) from native format stored in the IO diff --git a/Logic/Framework/GlobalState.cxx b/Logic/Framework/GlobalState.cxx index f02e92e2..a2bd0f96 100644 --- a/Logic/Framework/GlobalState.cxx +++ b/Logic/Framework/GlobalState.cxx @@ -92,6 +92,7 @@ ::GlobalState() m_PaintbrushSettings.volumetric = false; m_PaintbrushSettings.isotropic = false; m_PaintbrushSettings.chase = false; + m_PaintbrushSettings.direct = false; m_PaintbrushSettings.watershed.level = 0.2; m_PaintbrushSettings.watershed.smooth_iterations = 15; diff --git a/Logic/Framework/GlobalState.h b/Logic/Framework/GlobalState.h index 2bdb2929..0d40fb57 100644 --- a/Logic/Framework/GlobalState.h +++ b/Logic/Framework/GlobalState.h @@ -106,12 +106,15 @@ enum DisplayPanel enum ToolbarModeType { - CROSSHAIRS_MODE = 0, - NAVIGATION_MODE, - POLYGON_DRAWING_MODE, - PAINTBRUSH_MODE, - ROI_MODE, - ANNOTATION_MODE + CROSSHAIRS_MODE = 0, //0: btnCursorInspector + NAVIGATION_MODE, //1: btnZoomInspector + POLYGON_DRAWING_MODE, //2: btnPolygonInspector + PAINTBRUSH_MODE, //3: btnPaintbrushInspector + SNAKE_ROI_MODE, //4: btnSnakeInspector + JOIN_MODE, //5: btnJoinInspector + GLOBALWS_ROI_MODE, //6: btnSnakeInspector + GWSJOIN_MODE, //7: btnCursorInspector + ANNOTATION_MODE //8: btnSnakeInspector }; enum ToolbarMode3DType @@ -153,6 +156,7 @@ struct PaintbrushSettings bool volumetric; bool isotropic; bool chase; + bool direct; PaintbrushWatershedSettings watershed; }; diff --git a/Logic/Framework/IRISApplication.cxx b/Logic/Framework/IRISApplication.cxx index 64a3450d..cc66f07e 100644 --- a/Logic/Framework/IRISApplication.cxx +++ b/Logic/Framework/IRISApplication.cxx @@ -45,6 +45,7 @@ #include "IRISImageData.h" #include "IRISVectorTypesToITKConversion.h" #include "SNAPImageData.h" +#include "JOINImageData.h" #include "MeshManager.h" #include "MeshExportSettings.h" #include "SegmentationStatistics.h" @@ -59,6 +60,7 @@ #include "itkBSplineInterpolateImageFunction.h" #include "itkLinearInterpolateImageFunction.h" #include "itkWindowedSincInterpolateImageFunction.h" +#include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkFlipImageFilter.h" #include "itkConstantBoundaryCondition.h" @@ -118,6 +120,9 @@ ::IRISApplication() m_SNAPImageData = SNAPImageData::New(); m_SNAPImageData->SetParent(this); + m_JOINImageData = JOINImageData::New(); + m_JOINImageData->SetParent(this); + // Set the current IRIS pointer m_CurrentImageData = m_IRISImageData.GetPointer(); @@ -125,6 +130,7 @@ ::IRISApplication() // as our own events. Rebroadcaster::RebroadcastAsSourceEvent(m_IRISImageData, WrapperChangeEvent(), this); Rebroadcaster::RebroadcastAsSourceEvent(m_SNAPImageData, WrapperChangeEvent(), this); + Rebroadcaster::RebroadcastAsSourceEvent(m_JOINImageData, WrapperChangeEvent(), this); // TODO: should this also be a generic Wrapper Image Data change event? Rebroadcaster::RebroadcastAsSourceEvent(m_SNAPImageData, LevelSetImageChangeEvent(), this); @@ -240,6 +246,211 @@ ::InitializeSNAPImageData(const SNAPSegmentationROISettings &roi, InvokeEvent(LayerChangeEvent()); } +void +IRISApplication +::InitializeJOINImageData(const SNAPSegmentationROISettings &roi, int CnJMode, const char* FileName, + CommandType *progressCommand) +{ + assert(m_IRISImageData->IsMainLoaded()); + + // Create the JOIN image data object + m_JOINImageData->InitializeToROI(m_IRISImageData, roi, progressCommand); + + if(CnJMode == 0){//CnJ with "current segementation" + m_JOINImageData->InitializeJsrc();//fills Jsrc with zeros + } + else if(CnJMode == 1){//GWS + m_JOINImageData->InitializeWsrc(); + m_JOINImageData->InitializeJsrc();//fills Jsrc with zeros + } + else if(CnJMode == 2){//CnJ with "load from file" + assert(FileName); + typedef itk::ImageFileReader ReaderType; + typename ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(FileName); + try{ + reader->Update(); + m_JOINImageData->InitializeJsrc(reader->GetOutput()); + } + catch(itk::ExceptionObject &ex){ + std::cerr << ex << std::endl; + + ////ToDo: return from CnJ if an error occures (e.g. file does not exist) + m_JOINImageData->InitializeJsrc();//fills Jsrc with zeros + } + } + + // Initialize the source and destination images of the JOIN image data + m_JOINImageData->InitializeJdst(); + + // Pass the cleaned up segmentation image to SNAP + m_JOINImageData->SetSegmentationImage(m_JOINImageData->GetJdst()->GetImage()); + + // Remember the ROI object + m_GlobalState->SetSegmentationROISettings(roi); + + // Initialize JoinCopyFilter + m_JOINImageData->InitializeJoinCF(); + + // The set of layers has changed + InvokeEvent(LayerChangeEvent()); +} + +bool +IRISApplication +::ExecuteCnJCopy(JsrcImageWrapper::ImageType::IndexType SeedIndex) +{ + assert(m_JOINImageData->IsJsrcLoaded()); + + GlobalState *gs = GetGlobalState(); + + // Get the paint properties + LabelType drawing_color = gs->GetDrawingColorLabel(); + DrawOverFilter drawover = gs->GetDrawOverFilter(); + + m_JOINImageData->GetJoinCF()->SetSeedIndex(SeedIndex); + m_JOINImageData->GetJoinCF()->SetDrawingColor(drawing_color); + m_JOINImageData->GetJoinCF()->SetSeedActive(true); + m_JOINImageData->GetJoinCF()->Update(); + m_JOINImageData->GetJdst()->SetImage(m_JOINImageData->GetJoinCF()->GetOutput()); //needed because output has stolen the input's buffer and the input has no image buffer. + + InvokeEvent(LayerChangeEvent()); + return m_JOINImageData->GetJoinCF()->GetUpdateFlag(); +} + +void +IRISApplication +::CopySegementationToJsrc(const SNAPSegmentationROISettings &roi, + CommandType *progressCommand) +{ + assert(m_JOINImageData->IsJsrcLoaded()); + + ////creating (shallow) copy, should only be used for initialization of CnJ (GWS fills its Jsrc with WS) + ////avoids m_IRISImageData->GetSegmentation()->DeepCopyRegion in case JSRType != LabelType + + typedef LabelImageWrapper::ImageType SourceImageType; + typedef JsrcImageWrapper::ImageType TargetImageType; + + SourceImageType::Pointer source = m_IRISImageData->GetSegmentation()->GetImage(); + TargetImageType::Pointer target = m_JOINImageData->GetJsrc()->GetImage(); + + // Create iterators for copying from one to the other + typedef itk::ImageRegionConstIterator SourceIteratorType; + typedef itk::ImageRegionIterator TargetIteratorType; + //SourceIteratorType itSource(source,source->GetBufferedRegion()); + TargetIteratorType itTarget(target,target->GetBufferedRegion()); + SourceIteratorType itSource(source,roi.GetROI()); + //TargetIteratorType itTarget(target,roi.GetROI()); + + // Go through both iterators, copy the new over the old + itSource.GoToBegin(); + itTarget.GoToBegin(); + while(!itSource.IsAtEnd()) + { + itTarget.Set(itSource.Get());//needs no cast as JSRType >= LabelType + + ++itSource; + ++itTarget; + } + + // The target has been modified + target->Modified(); + + + InvokeEvent(LayerChangeEvent()); +} + +void +IRISApplication +::LoadImageToJsrc(const char* FileName, const SNAPSegmentationROISettings &roi, CommandType *progressCommand) +{ + assert(m_JOINImageData->IsJsrcLoaded()); + + typedef JsrcImageWrapper::ImageType TargetImageType; + TargetImageType::Pointer target = m_JOINImageData->GetJsrc()->GetImage(); + + typedef itk::ImageFileReader ReaderType; + typename ReaderType::Pointer reader = ReaderType::New(); + reader->SetFileName(FileName); + reader->Update(); + // target= reader->GetOutput(); + // target->DisconnectPipeline(); + + // // The target has been modified + // target->Modified(); + m_JOINImageData->SetJsrc(reader->GetOutput()); + + InvokeEvent(LayerChangeEvent()); +} + +void +IRISApplication +::CopySegementationToJdst(const SNAPSegmentationROISettings &roi, + CommandType *progressCommand) +{ + assert(m_JOINImageData->IsJdstLoaded()); + + ////creating shallow copy since Jdst already is initialized and points to m_LabelImage set by m_JOINImageData->SetSegmentationImage in InitializeJOINImageData + + typedef LabelImageWrapper::ImageType SourceImageType; + typedef JdstImageWrapper::ImageType TargetImageType; + + SourceImageType::Pointer source = m_IRISImageData->GetSegmentation()->GetImage(); + TargetImageType::Pointer target = m_JOINImageData->GetJdst()->GetImage(); + + // Create iterators for copying from one to the other + typedef itk::ImageRegionConstIterator SourceIteratorType; + typedef itk::ImageRegionIterator TargetIteratorType; + //SourceIteratorType itSource(source,source->GetBufferedRegion()); + TargetIteratorType itTarget(target,target->GetBufferedRegion()); + SourceIteratorType itSource(source,roi.GetROI()); + //TargetIteratorType itTarget(target,roi.GetROI()); + + // Go through both iterators, copy the new over the old + itSource.GoToBegin(); + itTarget.GoToBegin(); + while(!itSource.IsAtEnd()) + { + itTarget.Set(itSource.Get()); + + ++itSource; + ++itTarget; + } + + // The target has been modified + target->Modified(); + + + InvokeEvent(LayerChangeEvent()); +} + +void +IRISApplication +::ClearJdst() +{ + assert(m_JOINImageData->IsJdstLoaded()); + + ////clear Jdst manually + typedef JdstImageWrapper::ImageType TargetImageType; + + TargetImageType::Pointer target = m_JOINImageData->GetJdst()->GetImage(); + + typedef itk::ImageRegionIterator TargetIteratorType; + TargetIteratorType itTarget(target,target->GetLargestPossibleRegion()); + + itTarget.GoToBegin(); + while(!itTarget.IsAtEnd()) + { + itTarget.Set((LabelType) 0); + ++itTarget; + } + + // The target has been modified + target->Modified(); + + InvokeEvent(LayerChangeEvent()); +} + void IRISApplication ::SetDisplayGeometry(const IRISDisplayGeometry &dispGeom) @@ -259,6 +470,12 @@ ::SetDisplayGeometry(const IRISDisplayGeometry &dispGeom) m_SNAPImageData->SetDisplayGeometry(dispGeom); } + // Create the appropriate transform and pass it to the JOIN data + if(m_JOINImageData->IsMainLoaded()) + { + m_JOINImageData->SetDisplayGeometry(dispGeom); + } + // Invoke the corresponding event InvokeEvent(DisplayToAnatomyCoordinateMappingChangeEvent()); } @@ -707,6 +924,60 @@ ::UpdateIRISWithSnapImageData(CommandType *progressCommand) target->Modified(); } +void +IRISApplication +::UpdateIRISWithJOINImageData(CommandType *progressCommand) +{ + assert(IsJoinModeActive()); + + ////exchanging pointers (only works if UnloadAll does not unload Join layers) + //m_IRISImageData->SetSegmentationImage(m_JOINImageData->GetJdst()->GetImage()); + ////full copy + // Get pointers to the source and destination images + typedef JdstImageWrapper::ImageType SourceImageType; + typedef LabelImageWrapper::ImageType TargetImageType; + + // If the voxel size of the image does not match the voxel size of the + // main image, we need to resample the region + SourceImageType::Pointer source = m_JOINImageData->GetJdst()->GetImage(); + TargetImageType::Pointer target = m_IRISImageData->GetSegmentation()->GetImage(); + + // Construct are region of interest into which the result will be pasted + SNAPSegmentationROISettings roi = m_GlobalState->GetSegmentationROISettings(); + // the roi above can be wrong if a segmentation was loaded while itksnap is in a ROI-mode (e.g. Snake or gWS) + // the mismatch in the ROI-region index then causes the program to crash when the interators are initialized down below + // therefore disabled loading of segmentations during ROI-modes in MainImageWindow.cxx + + // Create iterators for copying from one to the other + typedef itk::ImageRegionConstIterator SourceIteratorType; + typedef itk::ImageRegionIterator TargetIteratorType; + SourceIteratorType itSource(source,source->GetLargestPossibleRegion()); + //SourceIteratorType itSource(source,roi.GetROI()); + TargetIteratorType itTarget(target,roi.GetROI()); + //TargetIteratorType itTarget(target,source->GetLargestPossibleRegion()); + + DrawOverFilter drawover = m_GlobalState->GetDrawOverFilter(); + + // Go through both iterators, copy the new over the old + itSource.GoToBegin(); + itTarget.GoToBegin(); + while(!itSource.IsAtEnd()){ + LabelType pxLabel= itTarget.Get(); + if(drawover.CoverageMode == PAINT_OVER_ALL || + (drawover.CoverageMode == PAINT_OVER_ONE && pxLabel == drawover.DrawOverLabel) || + (drawover.CoverageMode == PAINT_OVER_VISIBLE && pxLabel != 0)) + { + itTarget.Set(itSource.Get()); + } + + ++itSource; + ++itTarget; + } + + // The target has been modified + target->Modified(); +} + void IRISApplication ::SetCursorPosition(const Vector3ui cursor, bool force) @@ -829,9 +1100,6 @@ ::Redo() InvokeEvent(SegmentationChangeEvent()); } - - - void IRISApplication ::ReleaseSNAPImageData() @@ -842,6 +1110,16 @@ ::ReleaseSNAPImageData() m_SNAPImageData->UnloadAll(); } +void +IRISApplication +::ReleaseJOINImageData() +{ + assert(m_JOINImageData->IsMainLoaded() && + m_CurrentImageData != m_JOINImageData); + + m_JOINImageData->UnloadAll(); +} + void IRISApplication ::TransferCursor(GenericImageData *source, GenericImageData *target) @@ -874,8 +1152,14 @@ ::SetCurrentImageDataToIRIS() assert(m_IRISImageData); if(m_CurrentImageData != m_IRISImageData) { + if(m_CurrentImageData == m_JOINImageData){ + m_CurrentImageData = m_IRISImageData; + TransferCursor(m_JOINImageData, m_IRISImageData); + } + else{ m_CurrentImageData = m_IRISImageData; TransferCursor(m_SNAPImageData, m_IRISImageData); + } InvokeEvent(MainImageDimensionsChangeEvent()); // Set the selected layer ID to the main image @@ -908,6 +1192,28 @@ ::SetCurrentImageDataToSNAP() } } +void IRISApplication +::SetCurrentImageDataToJOIN() +{ + assert(m_JOINImageData->IsMainLoaded()); + if(m_CurrentImageData != m_JOINImageData) + { + // The cursor needs to be modified to point to the same location + // as before, or to the center of the image + TransferCursor(m_IRISImageData, m_JOINImageData); + + // Set the image data + m_CurrentImageData = m_JOINImageData; + + // Fire the event + InvokeEvent(MainImageDimensionsChangeEvent()); + + // Upon entering this mode, we need set the active tools + m_GlobalState->SetToolbarMode(JOIN_MODE); + m_GlobalState->SetToolbarMode3D(TRACKBALL_MODE); + } +} + int IRISApplication::GetImageDirectionForAnatomicalDirection(AnatomicalDirection iAnat) { std::string myrai = this->GetImageToAnatomyRAI(); @@ -1857,6 +2163,26 @@ IRISApplication::CreateSaveDelegateForLayer(ImageWrapperBase *layer, LayerRole r category = "Level Set Image"; } } + else if(role == JOIN_ROLE) + { + if(dynamic_cast(layer)) + { + history = "JsrcImage"; + category = "Join Source Image"; + } + + else if(dynamic_cast(layer)) + { + history = "JdstImage"; + category = "Join Destination Image"; + } + + else if(dynamic_cast(layer)) + { + history = "WsrcImage"; + category = "GWS Source Image"; + } + } // Create delegate SmartPtr delegate = DefaultSaveImageDelegate::New(); @@ -2215,6 +2541,11 @@ bool IRISApplication::IsSnakeModeActive() const return (m_CurrentImageData == m_SNAPImageData); } +bool IRISApplication::IsJoinModeActive() const +{ + return (m_CurrentImageData == m_JOINImageData); +} + bool IRISApplication::IsSnakeModeLevelSetActive() const { return IsSnakeModeActive() && m_SNAPImageData->IsSnakeLoaded(); diff --git a/Logic/Framework/IRISApplication.h b/Logic/Framework/IRISApplication.h index a7b7a543..3f55fd29 100644 --- a/Logic/Framework/IRISApplication.h +++ b/Logic/Framework/IRISApplication.h @@ -46,6 +46,7 @@ #include "SystemInterface.h" #include "UndoDataManager.h" #include "SNAPEvents.h" +#include "JOINImageData.h" //for JsrcImageWrapper::ImageType::IndexType // #include "itkImage.h" @@ -54,6 +55,7 @@ class GenericImageData; class IRISException; class IRISImageData; class SNAPImageData; +class JOINImageData; class MeshExportSettings; class GuidedNativeImageIO; class ThresholdSettings; @@ -152,6 +154,11 @@ class IRISApplication : public itk::Object */ irisGetMacro(SNAPImageData,SNAPImageData *); + /** + * Get image data related to Join operations + */ + irisGetMacro(JOINImageData,JOINImageData *); + /** * Get the image data currently used */ @@ -167,11 +174,21 @@ class IRISApplication : public itk::Object */ void SetCurrentImageDataToSNAP(); + /** + * Enter the JOIN mode + */ + void SetCurrentImageDataToJOIN(); + /** Whether we are currently in active contour mode or not */ bool IsSnakeModeActive() const; + /** + Whether we are currently in Join mode or not + */ + bool IsJoinModeActive() const; + /** * Whether there is currently a valid level set function */ @@ -298,6 +315,39 @@ class IRISApplication : public itk::Object void InitializeSNAPImageData(const SNAPSegmentationROISettings &roi, CommandType *progressCommand = NULL); + /** + * Initialize JOIN Image data using region of interest extents + */ + void InitializeJOINImageData(const SNAPSegmentationROISettings &roi, int CnJMode, const char* FileName, + CommandType *progressCommand = NULL); + + /** + * Execute CnJ CopyFilter + */ + bool ExecuteCnJCopy(JsrcImageWrapper::ImageType::IndexType SeedIndex); + + /** + * Copy Segmentation to Jsrc for JOIN-mode 0 + */ + void CopySegementationToJsrc(const SNAPSegmentationROISettings &roi, + CommandType *progressCommand = NULL); + + /** + * Load external Segmentation to Jsrc for JOIN-mode 2 + */ + void LoadImageToJsrc(const char* FileName, const SNAPSegmentationROISettings &roi, CommandType *progressCommand); + + /** + * Copy Segmentation to Jsrc for JOIN-mode 0 + */ + void CopySegementationToJdst(const SNAPSegmentationROISettings &roi, + CommandType *progressCommand = NULL); + + /** + * Clear Jdst for JOIN-mode 0 + */ + void ClearJdst(); + /** Enter given preprocessing mode. This activates the pipeline that can be used to provide automatic on-the-fly preview of the preprocessing result @@ -343,6 +393,12 @@ class IRISApplication : public itk::Object */ void UpdateIRISWithSnapImageData(CommandType *progressCommand = NULL); + /** + * Update IRIS image data with the segmentation contained in the JOIN image + * data. + */ + void UpdateIRISWithJOINImageData(CommandType *progressCommand = NULL); + /** * Get the segmentation label data */ @@ -356,6 +412,9 @@ class IRISApplication : public itk::Object /** Release the SNAP Image data */ void ReleaseSNAPImageData(); + /** Release the JOIN Image data */ + void ReleaseJOINImageData(); + /** Update the display-anatomy mapping as an RAI code */ void SetDisplayGeometry(const IRISDisplayGeometry &dispGeom); @@ -621,6 +680,7 @@ class IRISApplication : public itk::Object GenericImageData *m_CurrentImageData; SmartPtr m_IRISImageData; SmartPtr m_SNAPImageData; + SmartPtr m_JOINImageData; // Color label data SmartPtr m_ColorLabelTable; diff --git a/Logic/Framework/JOINImageData.cxx b/Logic/Framework/JOINImageData.cxx new file mode 100644 index 00000000..40809ff3 --- /dev/null +++ b/Logic/Framework/JOINImageData.cxx @@ -0,0 +1,348 @@ +/*========================================================================= + + Program: ITK-SNAP + Module: $RCSfile: SNAPImageData.cxx,v $ + Language: C++ + Date: $Date: 2011/04/18 17:35:30 $ + Version: $Revision: 1.11 $ + Copyright (c) 2007 Paul A. Yushkevich + + This file is part of ITK-SNAP + + ITK-SNAP is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + ----- + + Copyright (c) 2003 Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ + +#include "IRISApplication.h" +#include "JOINImageData.h" +//#include "itkJCFilterWatcher.h" +#include + + +void FilterEventHandlerITK2(itk::Object *caller, const itk::EventObject &event, void*){ + + const itk::ProcessObject* filter = static_cast(caller); + + if(itk::ProgressEvent().CheckEvent(&event)) + fprintf(stderr, "\r%s progress: %5.1f%%", filter->GetNameOfClass(), 100.0 * filter->GetProgress());//stderr is flushed directly + else if(itk::EndEvent().CheckEvent(&event)) + std::cerr << std::endl << std::flush; + } + + +JOINImageData +::JOINImageData(){ + } + + +JOINImageData +::~JOINImageData(){ + } + + +/* ============================= + Join source Image + ============================= */ + +void +JOINImageData +::InitializeJsrc(JsrcImageType *newJsrcImage){ + // The Grey image wrapper should be present + assert(m_MainImageWrapper->IsInitialized()); + + // Intialize Jsrc based on the current grey image + if(m_JsrcWrapper.IsNull()) + { + m_JsrcWrapper = JsrcImageWrapper::New(); + m_JsrcWrapper->SetDefaultNickname("Join Source Image"); + PushBackImageWrapper(JOIN_ROLE, m_JsrcWrapper.GetPointer()); + } + + if(newJsrcImage) + m_JsrcWrapper->InitializeToWrapper(m_MainImageWrapper, newJsrcImage); + else + m_JsrcWrapper->InitializeToWrapper(m_MainImageWrapper, (JSRType) 0); + m_JsrcWrapper->SetSticky(true); //overlay, ie no separate tile + m_JsrcWrapper->SetAlpha(0.5); + + InvokeEvent(LayerChangeEvent()); + } + +JsrcImageWrapper* +JOINImageData +::GetJsrc(){ + // Make sure it exists + assert(IsJsrcLoaded()); + return m_JsrcWrapper; + } + +void +JOINImageData +::SetJsrc(JsrcImageType *newJsrcImage){ + ////from ./Logic/Framework/GenericImageData.cxx:244:::SetSegmentationImage + // Check that the image matches the size of the grey image + assert(m_MainImageWrapper->IsInitialized()); + + assert(m_MainImageWrapper->GetBufferedRegion() == + newJsrcImage->GetBufferedRegion()); + + // Pass the image to the wrapper + m_JsrcWrapper->SetImage(newJsrcImage); + m_JsrcWrapper->GetImage()->Modified();// This makes sure that the IsDrawable() of the wrapper returns true, essential for showing up in the the SliceView (l.284 GenericSliceRenderer.cxx) + + // Sync up spacing between the main and label image + newJsrcImage->SetSpacing(m_MainImageWrapper->GetImageBase()->GetSpacing()); + newJsrcImage->SetOrigin(m_MainImageWrapper->GetImageBase()->GetOrigin()); + } + +bool +JOINImageData +::IsJsrcLoaded(){ + return m_JsrcWrapper && m_JsrcWrapper->IsInitialized(); + } + + +/* ============================= + Join destination Image + ============================= */ + +void +JOINImageData +::InitializeJdst(){ + assert(IsJsrcLoaded()); + + // If a initialization wrapper does not exist, create it + if(!m_JdstWrapper) + { + m_JdstWrapper = JdstImageWrapper::New(); + m_JdstWrapper->SetDefaultNickname("Join Destination Image"); + PushBackImageWrapper(JOIN_ROLE, m_JdstWrapper.GetPointer()); + } + + m_JdstWrapper->GetDisplayMapping()->SetLabelColorTable(m_Parent->GetColorLabelTable()); + m_JdstWrapper->InitializeToWrapper(m_MainImageWrapper, (LabelType) 0); + m_JdstWrapper->SetSticky(true);//sticky is set with SetJdstSticky + m_JdstWrapper->SetAlpha(0.5); + + InvokeEvent(LayerChangeEvent()); + } + +JdstImageWrapper* +JOINImageData +::GetJdst(){ + assert(IsJdstLoaded()); + return m_JdstWrapper; + } + +bool +JOINImageData +::IsJdstLoaded() +{ + return (m_JdstWrapper && m_JdstWrapper->IsInitialized()); +} + +void +JOINImageData +::SetJdstSticky(bool sticky){ + m_JdstWrapper->SetSticky(sticky); + } + +/* ============================= + GWS source Image + ============================= */ + +void +JOINImageData +::InitializeWsrc(){ + // The Grey image wrapper should be present + assert(m_MainImageWrapper->IsInitialized()); + + // Intialize Wsrc based on the current grey image + if(m_WsrcWrapper.IsNull()) + { + m_WsrcWrapper = WsrcImageWrapper::New(); + m_WsrcWrapper->SetDefaultNickname("GWS Source Image"); + PushBackImageWrapper(JOIN_ROLE, m_WsrcWrapper.GetPointer()); + } + + m_WsrcWrapper->InitializeToWrapper(m_MainImageWrapper, (WSRType) 0); + m_WsrcWrapper->SetSticky(true); //sticky is set with SetWsrcSticky + m_WsrcWrapper->SetAlpha(0.5); + + InvokeEvent(LayerChangeEvent()); + } + +WsrcImageWrapper* +JOINImageData +::GetWsrc(){ + // Make sure it exists + assert(IsWsrcLoaded()); + return m_WsrcWrapper; + } + +void +JOINImageData +::SetWsrc(WsrcImageType *newWsrcImage){ + ////from ./Logic/Framework/GenericImageData.cxx:244:::SetSegmentationImage + // Check that the image matches the size of the grey image + assert(m_MainImageWrapper->IsInitialized()); + + assert(m_MainImageWrapper->GetBufferedRegion() == + newWsrcImage->GetBufferedRegion()); + + // Pass the image to the wrapper + m_WsrcWrapper->SetImage(newWsrcImage); + m_WsrcWrapper->GetImage()->Modified();// This makes sure that the IsDrawable() of the wrapper returns true, essential for showing up in the the SliceView (l.284 GenericSliceRenderer.cxx) + + // Sync up spacing between the main and label image + newWsrcImage->SetSpacing(m_MainImageWrapper->GetImageBase()->GetSpacing()); + newWsrcImage->SetOrigin(m_MainImageWrapper->GetImageBase()->GetOrigin()); + } + +bool +JOINImageData +::IsWsrcLoaded(){ + return m_WsrcWrapper && m_WsrcWrapper->IsInitialized(); + } + +void +JOINImageData +::SetWsrcSticky(bool sticky){ + m_WsrcWrapper->SetSticky(sticky); + } + +/**********************************/ + +void +JOINImageData +::InitializeToROI(GenericImageData *source, + const SNAPSegmentationROISettings &roi, + itk::Command *progressCommand){ + // Get the source main wrapper + ImageWrapperBase *srcMain = source->GetMain(); + + // Extract the ROI into a generic type + SmartPtr roiMain = srcMain->ExtractROI(roi, progressCommand); + //source->GetMain()->GetImage(); //full grey, only roi GWS processed + + // Assign the new wrapper to the target + this->SetMainImageInternal(roiMain); + + // Copy metadata + this->CopyLayerMetadata(this->GetMain(), source->GetMain()); + + // Repeat all of this for the overlays + for(LayerIterator lit = source->GetLayers(OVERLAY_ROLE); + !lit.IsAtEnd(); ++lit) + { + // Do the same for all the anatomic wrappers + SmartPtr roiOvl = + lit.GetLayer()->ExtractROI(roi, progressCommand); + + // Add the overlay + this->AddOverlayInternal(roiOvl); + + // Copy metadata + this->CopyLayerMetadata(this->GetLastOverlay(), lit.GetLayer()); + } + } + +void JOINImageData::CopyLayerMetadata( + ImageWrapperBase *target, ImageWrapperBase *source){ + // Nickname + target->SetDefaultNickname(source->GetNickname()); + + // This is a little bit of overhead, but not enough to be a big deal: + // we just save the display mapping to a Registry and then restore it + // in the target wrapper. + Registry folder; + source->GetDisplayMapping()->Save(folder); + target->GetDisplayMapping()->Restore(folder); + + // Threshold settings. These should be copied for each scalar component + if(source->IsScalar()) + { + target->SetUserData("ThresholdSettings", source->GetUserData("ThresholdSettings")); + } + else + { + // Copy threshold settings for all the scalar components + VectorImageWrapperBase *v_source = dynamic_cast(source); + VectorImageWrapperBase *v_target = dynamic_cast(target); + + for(ScalarRepresentationIterator it(v_source); !it.IsAtEnd(); ++it) + { + ImageWrapperBase *c_source = v_source->GetScalarRepresentation(it); + ImageWrapperBase *c_target = v_target->GetScalarRepresentation(it); + c_target->SetUserData("ThresholdSettings", c_source->GetUserData("ThresholdSettings")); + } + } + + // TODO: alpha, stickiness? + } + +void JOINImageData::UnloadAll(){ + // Unload all the data + this->UnloadOverlays(); + this->UnloadMainImage(); //do not unload if Main just points to IRIS-Main!!! + + // We need to unload all the JOIN layers + while(this->m_Wrappers[JOIN_ROLE].size()) + PopBackImageWrapper(JOIN_ROLE); + m_JsrcWrapper = NULL; + m_JdstWrapper = NULL; + m_WsrcWrapper = NULL; + + InvokeEvent(LayerChangeEvent()); + } + +void +JOINImageData +::InitializeJoinCF(){ + ////make sure Jsrc and Jdst exist + assert(IsJsrcLoaded()); + assert(IsJdstLoaded()); + + ////Initialize JoinCopyFilter + m_JoinCF= JoinCopyFilterType::New(); + + m_JoinCF->SetJsrc(m_JsrcWrapper->GetImage()); + m_JoinCF->SetJdst(m_JdstWrapper->GetImage()); + //m_JdstWrapper->SetImage(m_JoinCF->GetOutput()); //causes segfault due to missing transform of uninitialized output + m_JoinCF->InPlaceOn(); //makes 2nd input (SetJdst) be the output + + itk::CStyleCommand::Pointer eventCallbackITK; + eventCallbackITK = itk::CStyleCommand::New(); + eventCallbackITK->SetCallback(FilterEventHandlerITK2); + + m_JoinCF->AddObserver(itk::ProgressEvent(), eventCallbackITK); + m_JoinCF->AddObserver(itk::EndEvent(), eventCallbackITK); + + } + +JOINImageData::JoinCopyFilterPointer +JOINImageData::GetJoinCF(){ + ////todo: introduce check if m_JoinCF was initialized + return m_JoinCF; + } + diff --git a/Logic/Framework/JOINImageData.h b/Logic/Framework/JOINImageData.h new file mode 100644 index 00000000..363863f7 --- /dev/null +++ b/Logic/Framework/JOINImageData.h @@ -0,0 +1,122 @@ +/*========================================================================= + + Program: ITK-SNAP + Module: $RCSfile: SNAPImageData.h,v $ + Language: C++ + Date: $Date: 2009/01/23 20:09:38 $ + Version: $Revision: 1.4 $ + Copyright (c) 2007 Paul A. Yushkevich + + This file is part of ITK-SNAP + + ITK-SNAP is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + + ----- + + Copyright (c) 2003 Insight Software Consortium. All rights reserved. + See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notices for more information. + +=========================================================================*/ +#ifndef __JOINImageData_h_ +#define __JOINImageData_h_ + +#include "SNAPCommon.h" + +#include "GenericImageData.h" +#include "itkImageAdaptor.h" +#include "itkJoinCopyFilter.h" + +/** + * \class JOINImageData + * \brief Wrapper around the JOIN automatic segmentation pipelines. + * + * This class encapsulates several images used in the JOIN application + */ +class JOINImageData : public GenericImageData +{ +public: + irisITKObjectMacro(JOINImageData, GenericImageData) + + // The type of the internal level set image + typedef Superclass::AnatomicImageType AnatomicImageType; + typedef JsrcImageWrapper::ImageType JsrcImageType; + typedef JdstImageWrapper::ImageType JdstImageType; + typedef WsrcImageWrapper::ImageType WsrcImageType; + + typedef itk::JoinCopyFilter JoinCopyFilterType; + typedef JoinCopyFilterType::Pointer JoinCopyFilterPointer; + + /** Initialize to a ROI from another image data object */ + void InitializeToROI(GenericImageData *source, + const SNAPSegmentationROISettings &roi, + itk::Command *progressCommand); + + /** Copy nickname, settings, and other such junk from IRIS to SNAP during + * initialization */ + void CopyLayerMetadata(ImageWrapperBase *target, ImageWrapperBase *source); + + /** + Unload all images in the JOIN image data, releasing memory and returning + this object to initial state. + */ + void UnloadAll(); + + void InitializeJsrc(JsrcImageType *newJsrcImage= NULL); + JsrcImageWrapper* GetJsrc(); + void SetJsrc(JsrcImageType *newJsrcImage); + bool IsJsrcLoaded(); + + void InitializeJdst(); + JdstImageWrapper* GetJdst(); + bool IsJdstLoaded(); + void SetJdstSticky(bool sticky); + + void InitializeWsrc(); + WsrcImageWrapper* GetWsrc(); + void SetWsrc(WsrcImageType *newWsrcImage); + bool IsWsrcLoaded(); + void SetWsrcSticky(bool sticky); + + void InitializeJoinCF(); + JoinCopyFilterType::Pointer GetJoinCF(); + +protected: + + JOINImageData(); + ~JOINImageData(); + + // Join source image + SmartPtr m_JsrcWrapper; + + // Join destination image + SmartPtr m_JdstWrapper; + + // GWS source image + SmartPtr m_WsrcWrapper; + + JoinCopyFilterPointer m_JoinCF; + +}; + + + + + + + +#endif diff --git a/Logic/Framework/LayerAssociation.txx b/Logic/Framework/LayerAssociation.txx index dbf5f320..ae6d10bd 100644 --- a/Logic/Framework/LayerAssociation.txx +++ b/Logic/Framework/LayerAssociation.txx @@ -6,6 +6,7 @@ #include "GenericImageData.h" #include "IRISImageData.h" #include "SNAPImageData.h" +#include "JOINImageData.h" #include "ImageWrapperBase.h" template @@ -36,9 +37,10 @@ LayerAssociation { // Iterate over all of the image data objects in IRISApplication GenericImageData *id[] = {m_Source->GetIRISImageData(), + m_Source->GetJOINImageData(), m_Source->GetSNAPImageData()}; - for(int k = 0; k < 2; k++) + for(int k = 0; k < 3; k++) { if(id[k]) { diff --git a/Logic/ImageWrapper/CommonRepresentationPolicy.cxx b/Logic/ImageWrapper/CommonRepresentationPolicy.cxx index 9e35e6a0..ec61bb7a 100644 --- a/Logic/ImageWrapper/CommonRepresentationPolicy.cxx +++ b/Logic/ImageWrapper/CommonRepresentationPolicy.cxx @@ -94,6 +94,8 @@ template class CastingScalarImageWrapperCommonRepresentation< template class CastingScalarImageWrapperCommonRepresentation< GreyType, GreyAnatomicScalarImageWrapperTraits >; +template class CastingScalarImageWrapperCommonRepresentation< + GreyType, WsrcImageWrapperTraits >; diff --git a/Logic/ImageWrapper/DisplayMappingPolicy.cxx b/Logic/ImageWrapper/DisplayMappingPolicy.cxx index 4761a9c9..efe4c445 100644 --- a/Logic/ImageWrapper/DisplayMappingPolicy.cxx +++ b/Logic/ImageWrapper/DisplayMappingPolicy.cxx @@ -15,6 +15,7 @@ #include "itkUnaryFunctorImageFilter.h" #include "InputSelectionImageFilter.h" #include "Rebroadcaster.h" +#include /* =============================================================== @@ -96,6 +97,79 @@ ::GetLabelColorTable() const } +/* =============================================================== + JsrcDisplayMappingPolicy implementation + =============================================================== */ + +template +JsrcDisplayMappingPolicy +::JsrcDisplayMappingPolicy() +{ + m_Wrapper = NULL; + + for(int i = 0; i < 3; i++) + { + m_RGBAFilter[i] = RGBAFilterType::New(); + m_RGBAFilter[i]->SetFunctor(m_Functor); + } +} + +template +JsrcDisplayMappingPolicy +::~JsrcDisplayMappingPolicy() +{ + +} + +template +void +JsrcDisplayMappingPolicy +::Initialize(WrapperType *wrapper) +{ + // Initialize the wrapper + m_Wrapper = wrapper; + + for(int i = 0; i < 3; i++) + { + m_RGBAFilter[i]->SetInput(wrapper->GetSlice(i)); + } +} + +template +void +JsrcDisplayMappingPolicy +::UpdateImagePointer(ImageType *image) +{ + // Nothing to do here, since we are connected to the slices? +} + +template +typename JsrcDisplayMappingPolicy::DisplaySlicePointer +JsrcDisplayMappingPolicy +::GetDisplaySlice(unsigned int slice) +{ + return m_RGBAFilter[slice]->GetOutput(); +} + +template +inline typename JsrcDisplayMappingPolicy::DisplayPixelType +JsrcDisplayMappingPolicy::MappingFunctor +::operator()(PixelType in) +{ + itk::RGBPixel RGBp; + DisplayPixelType RGBAp; + itk::Functor::ScalarToRGBPixelFunctor mFunctor; + + RGBp= mFunctor(in); + RGBAp[0]= RGBp[0]; + RGBAp[1]= RGBp[1]; + RGBAp[2]= RGBp[2]; + RGBAp[3]= (in == 0) ? 0 : itk::NumericTraits::max(); //make label 0 fully transparent + + return RGBAp; +} + + /* =============================================================== CachingCurveAndColorMapDisplayMappingPolicy implementation =============================================================== */ @@ -958,6 +1032,8 @@ ::Restore(Registry &folder) template class ColorLabelTableDisplayMappingPolicy; +template class JsrcDisplayMappingPolicy; +template class LinearColorMapDisplayMappingPolicy; template class LinearColorMapDisplayMappingPolicy; template class LinearColorMapDisplayMappingPolicy; diff --git a/Logic/ImageWrapper/DisplayMappingPolicy.h b/Logic/ImageWrapper/DisplayMappingPolicy.h index 0a96a583..4906a586 100644 --- a/Logic/ImageWrapper/DisplayMappingPolicy.h +++ b/Logic/ImageWrapper/DisplayMappingPolicy.h @@ -105,6 +105,76 @@ class ColorLabelTableDisplayMappingPolicy WrapperType *m_Wrapper; }; + /** + * JsrcDisplayMappingPolicy is based on + * ColorLabelTableDisplayMappingPolicy and LinearColorMapDisplayMappingPolicy + */ +class AbstractJsrcDisplayMappingPolicy : public AbstractDisplayMappingPolicy +{ +public: + + irisITKAbstractObjectMacro(AbstractJsrcDisplayMappingPolicy, + AbstractDisplayMappingPolicy) +}; + +namespace itk { + template + class UnaryFunctorImageFilter; +} + +template +class JsrcDisplayMappingPolicy + : public AbstractJsrcDisplayMappingPolicy +{ +public: + + irisITKObjectMacro(JsrcDisplayMappingPolicy, + AbstractJsrcDisplayMappingPolicy) + + typedef typename TWrapperTraits::WrapperType WrapperType; + typedef typename TWrapperTraits::ImageType ImageType; + typedef typename ImageType::PixelType PixelType; + + typedef itk::Image InputSliceType; + typedef ImageWrapperBase::DisplaySliceType DisplaySliceType; + typedef ImageWrapperBase::DisplaySlicePointer DisplaySlicePointer; + typedef ImageWrapperBase::DisplayPixelType DisplayPixelType; + + void Initialize(WrapperType *wrapper); + void UpdateImagePointer(ImageType *image); + + DisplaySlicePointer GetDisplaySlice(unsigned int slice); + + virtual IntensityCurveInterface *GetIntensityCurve() const { return NULL; } + virtual ColorMap *GetColorMap() const { return NULL; } + + virtual void Save(Registry &folder) {} + virtual void Restore(Registry &folder) {} + +protected: + + JsrcDisplayMappingPolicy(); + ~JsrcDisplayMappingPolicy(); + + class MappingFunctor + { + public: + DisplayPixelType operator()(PixelType in); + bool operator != (const MappingFunctor &f) const {return false;} //needed by UnaryFunctorImageFilter even tough ScalarToRGBPixelFunctor does not have this operator + }; + + // it is not possible to use ScalarToRGBPixelFunctor directly here + // because the expected output of MappingFunctor is RGBA + typedef itk::UnaryFunctorImageFilter + RGBAFilterType; + typedef SmartPtr RGBAFilterPointer; + + RGBAFilterPointer m_RGBAFilter[3]; + MappingFunctor m_Functor; + + WrapperType *m_Wrapper; +}; + /** * @brief The parent class for the policies that involve curve-based mappings, * for both scalar and vector images. diff --git a/Logic/ImageWrapper/ImageWrapper.cxx b/Logic/ImageWrapper/ImageWrapper.cxx index cf88d0bb..b1dd8eb3 100644 --- a/Logic/ImageWrapper/ImageWrapper.cxx +++ b/Logic/ImageWrapper/ImageWrapper.cxx @@ -1418,6 +1418,8 @@ ::DeepCopyRegion(const SNAPSegmentationROISettings &roi, template class ImageWrapper; template class ImageWrapper; template class ImageWrapper; +template class ImageWrapper; +template class ImageWrapper; template class ImageWrapper, VectorImageWrapperBase>; template class ImageWrapper, ScalarImageWrapperBase>; diff --git a/Logic/ImageWrapper/ImageWrapper.h b/Logic/ImageWrapper/ImageWrapper.h index 94e62865..310e23e2 100644 --- a/Logic/ImageWrapper/ImageWrapper.h +++ b/Logic/ImageWrapper/ImageWrapper.h @@ -161,7 +161,7 @@ class ImageWrapper : public TBase * source wrapper, otherwise, it's equivalent to SetImage() */ virtual void InitializeToWrapper( - const ImageWrapperBase *source, ImageType *image, ImageBaseType *refSpace, ITKTransformType *tran); + const ImageWrapperBase *source, ImageType *image, ImageBaseType *refSpace= NULL, ITKTransformType *tran= NULL); /** Get a unique id for this wrapper. All wrappers ever created have diff --git a/Logic/ImageWrapper/ImageWrapperTraits.h b/Logic/ImageWrapper/ImageWrapperTraits.h index 55302ec2..7dce6cfa 100644 --- a/Logic/ImageWrapper/ImageWrapperTraits.h +++ b/Logic/ImageWrapper/ImageWrapperTraits.h @@ -51,6 +51,53 @@ class LabelImageWrapperTraits itkStaticConstMacro(PipelineOutput, bool, false); }; +class JsrcImageWrapperTraits +{ +public: + typedef JsrcImageWrapperTraits Self; + + typedef ScalarImageWrapper WrapperType; + + typedef JSRType ComponentType; + typedef itk::Image ImageType; + + typedef LinearInternalToNativeIntensityMapping NativeIntensityMapping; + typedef JsrcDisplayMappingPolicy DisplayMapping; + typedef NullScalarImageWrapperCommonRepresentation CommonRepresentationPolicy; + + // Whether this image is shown on top of all other layers by default + itkStaticConstMacro(StickyByDefault, bool, false); + + // Whether this image is produced from another by a pipeline (e.g., speed image) + itkStaticConstMacro(PipelineOutput, bool, true); +}; + +class WsrcImageWrapperTraits +{ +public: + typedef WsrcImageWrapperTraits Self; + + typedef ScalarImageWrapper WrapperType; + + typedef WSRType ComponentType; + typedef itk::Image ImageType; + + typedef IdentityInternalToNativeIntensityMapping NativeIntensityMapping; + typedef LinearColorMapDisplayMappingPolicy DisplayMapping; + typedef NullScalarImageWrapperCommonRepresentation CommonRepresentationPolicy; + + static void GetFixedIntensityRange(float &min, float &max) + { min = -100.0; max = 100.0; } + + itkStaticConstMacro(DefaultColorMap, ColorMap::SystemPreset, ColorMap::COLORMAP_RWB); + + // Whether this image is shown on top of all other layers by default + itkStaticConstMacro(StickyByDefault, bool, true); + + // Whether this image is produced from another by a pipeline (e.g., speed image) + itkStaticConstMacro(PipelineOutput, bool, true); +}; + class SpeedImageWrapperTraits { public: @@ -241,6 +288,9 @@ typedef AnatomicImageWrapperTraits::WrapperType AnatomicImageWrapper; typedef AnatomicScalarImageWrapperTraits::WrapperType AnatomicScalarImageWrapper; typedef LabelImageWrapperTraits::WrapperType LabelImageWrapper; typedef SpeedImageWrapperTraits::WrapperType SpeedImageWrapper; +typedef JsrcImageWrapperTraits::WrapperType JsrcImageWrapper; +typedef LabelImageWrapperTraits::WrapperType JdstImageWrapper; +typedef WsrcImageWrapperTraits::WrapperType WsrcImageWrapper; typedef LevelSetImageWrapperTraits::WrapperType LevelSetImageWrapper; diff --git a/Logic/ImageWrapper/ScalarImageWrapper.cxx b/Logic/ImageWrapper/ScalarImageWrapper.cxx index 6ac44ffb..169f5065 100644 --- a/Logic/ImageWrapper/ScalarImageWrapper.cxx +++ b/Logic/ImageWrapper/ScalarImageWrapper.cxx @@ -359,6 +359,8 @@ ::GetHistogram(size_t nBins) template class ScalarImageWrapper; template class ScalarImageWrapper; template class ScalarImageWrapper; +template class ScalarImageWrapper; +template class ScalarImageWrapper; template class ScalarImageWrapper< ComponentImageWrapperTraits >; template class ScalarImageWrapper< AnatomicScalarImageWrapperTraits >; diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index a347e7ad..782e1229 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -59,7 +59,8 @@ on usability. - Command tooltips have been redesigned and now include information on the shortcut key to activate each command. In addion, tooltips for the tools in - the main palette describe the actions of each mouse button. + the main palette describe the actions of each mouse button. Many new shortcuts + have been added. - Cleaned up the behavior of mouse buttons and mouse scrolling to be more consistent between modes. Added Shift-scroll action, which scrolls through @@ -83,6 +84,13 @@ on usability. and a slider for "classifier bias", which allows you to bias the classfier output more toward the foreground class or background classes. + - Added the ability to select more than one class as the foreground class in + the random forest classification mode. This is powerful when the foreground + object has heterogeneous intensity. + + - Training examples drawn in random forest classificaiton mode are now retained + and can be reused to label multiple structures. + 1.1.2. Programmatic Improvements diff --git a/todo b/todo new file mode 100644 index 00000000..fc68680c --- /dev/null +++ b/todo @@ -0,0 +1,29 @@ +ToDo: + +- optimize CopySegementationToJdst and CopySegementationToJsrc +-- RMB functionality in JoinCopyFilter + +- combine mode 2 with mode 0 and add extra button for "LoadImageToJsrc" +-- restructure JoinDataPanel to have two main buttons: CnJ from Seg. or File OR CnJ from gWS +- add 'n' for next smallest unused label see eg m_Wrapper->GetHistogram(nBins) in ./Logic/ImageWrapper/DisplayMappingPolicy.cxx +- adjust GUI: +-- text on gWS range: max available but causes recompute +-- gWS move use file/overlay to bottom +- set gWS input only if it changed on prepro. page +- make UnDo for CnJ/gWS possible +- destinguish RMB click from RMB drag (zoom) +- let user choose which color representation should be used for Jsrc +- make GUI parts more fool prove +- add a GUI progress for current stderr progress +- add an invert option for direct mode, see e.g. InvertFunctor in SNAPImageData +- make contrast and LUT adjustment possible for e.g. GWS Source Image +- make it possible to "save" gWS state, e.g. WS regions and hirarchy such that no recomp. is necessary when gWS state is loaded (difficult, possibly not possible) + +- add MorphlogicalWS from all Labels into PB, result as overlay +- change "load from file" to "load from overlay" in CnJ and gWS + + +Done: + +-implement JoinCopyFilter (templated over dimension) +-- multi-threaded ImageRegionIterator