diff --git a/Modules/BoundingShape/CMakeLists.txt b/Modules/BoundingShape/CMakeLists.txt new file mode 100644 index 0000000000..933d5a7040 --- /dev/null +++ b/Modules/BoundingShape/CMakeLists.txt @@ -0,0 +1,6 @@ +MITK_CREATE_MODULE( + INCLUDE_DIRS PRIVATE src/DataManagement src/Interactions src/IO src/Rendering + DEPENDS MitkCore MitkDataTypesExt MitkAlgorithmsExt + WARNINGS_AS_ERRORS +) + diff --git a/Modules/BoundingShape/files.cmake b/Modules/BoundingShape/files.cmake new file mode 100644 index 0000000000..b14a0a8d39 --- /dev/null +++ b/Modules/BoundingShape/files.cmake @@ -0,0 +1,15 @@ +file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") + +set(CPP_FILES + DataManagement/mitkBoundingShapeUtil.cpp + DataManagement/mitkBoundingShapeCropper.cpp + DataManagement/mitkBoundingShapeObjectFactory.cpp + Interactions/mitkBoundingShapeInteractor.cpp + Rendering/mitkBoundingShapeVtkMapper2D.cpp + Rendering/mitkBoundingShapeVtkMapper3D.cpp +) + +set(RESOURCE_FILES + Interactions/BoundingShapeMouseConfig.xml + Interactions/BoundingShapeInteraction.xml +) diff --git a/Modules/BoundingShape/include/mitkBoundingShapeCropper.h b/Modules/BoundingShape/include/mitkBoundingShapeCropper.h new file mode 100644 index 0000000000..72f9eb8438 --- /dev/null +++ b/Modules/BoundingShape/include/mitkBoundingShapeCropper.h @@ -0,0 +1,164 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#ifndef BOUNDINGSHAPECROPPER_H +#define BOUNDINGSHAPECROPPER_H + +#include "mitkAlgorithmsExtExports.h" +#include "mitkBoundingShapeCropper.h" +#include "MitkBoundingShapeExports.h" +#include "mitkCommon.h" +#include "mitkDataTypesExtExports.h" +#include "mitkGeometryData.h" +#include "mitkImageToImageFilter.h" +#include "mitkImageTimeSelector.h" +#include "mitkImageAccessByItk.h" + +#include "itkImage.h" + +namespace mitk { + + /** Documentation + * @brief Crops or masks an Boundingbox defined by GeometryData out of an mitk Image + * + * Input Parameters are a mitk::GeometryData and an mitk::Image + * Masking: Pixel on the outside of the bounding box will have a pixelvalue of m_OutsideValue + * Cropping: Output image has the same size as the bounding box + */ + //## @ingroup Process + class MITKBOUNDINGSHAPE_EXPORT BoundingShapeCropper : public ImageToImageFilter + { + public: + mitkClassMacro(BoundingShapeCropper, ImageToImageFilter); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + /** + * @brief Set geometry of the bounding object + */ + void SetGeometry(const mitk::GeometryData* geometry); + /** + * @brief Get geometry of the bounding object + */ + //const mitk::GeometryData* GetGeometryData() const; + + + /** + * @brief Sets and Gets the outside value for masking + */ + itkSetMacro(OutsideValue, ScalarType); + itkGetMacro(OutsideValue, ScalarType); + /** + * @brief Sets and Gets whether a masking or cropping needs to be performed + */ + itkSetMacro(UseWholeInputRegion, bool); + itkGetMacro(UseWholeInputRegion, bool); + + /** + * @brief Sets and Gets the current timestep for images with 4 dimensons + */ + itkSetMacro(CurrentTimeStep, ScalarType); + itkGetMacro(CurrentTimeStep, ScalarType); + /** + *@brief Sets and Gets whether only one timestep is cropped / masked + */ + itkSetMacro(UseCropTimeStepOnly, bool); + itkGetMacro(UseCropTimeStepOnly, bool); + + protected: + BoundingShapeCropper(); + virtual ~BoundingShapeCropper(); + + virtual const PixelType GetOutputPixelType(); + + /** + * @brief Reimplemented from ImageToImageFilter + */ + virtual void GenerateInputRequestedRegion() override; + virtual void GenerateOutputInformation() override; + virtual void GenerateData() override; + + /** + * @brief Template Function for cropping and masking images with scalar pixel type + */ + template < typename TPixel, unsigned int VImageDimension > + void CutImage(itk::Image< TPixel, VImageDimension >* inputItkImage, int timeStep); + /** + * @brief Template Function for cropping and masking images with rgb pixel type + */ + template < typename TPixel, unsigned int VImageDimension > + void CutImage(itk::Image, VImageDimension >* inputItkImage, int timeStep); + /** + * @brief Template Function for cropping and masking images with vector pixel type + **/ + /* template < typename TPixel, unsigned int VImageDimension, typename TOutputPixel > + void CutImage(itk::VectorImage, VImageDimension >* itkImage, int boTimeStep); +*/ + /** + *@brief Process the image and create the output + **/ + virtual void ComputeData(mitk::Image* input3D, int boTimeStep); + + // virtual void ComputeData(mitk::LabelSetImage* image, int boTimeStep); + + private: + /** + *@brief GeometryData Type to capsulate all necessary components of the bounding object + **/ + mitk::GeometryData::Pointer m_Geometry; + + /** + * @brief scalar value for outside pixels (default: 0) + */ + ScalarType m_OutsideValue; + + /** + * @brief Use m_UseCropTimeStepOnly for only cropping a single time step(default: \a false) + */ + bool m_UseCropTimeStepOnly; + + /** + * @brief Current time step displayed + */ + unsigned int m_CurrentTimeStep; + /** + * @brief Use m_UseWholeInputRegion for deciding whether a cropping or masking will be performed + */ + bool m_UseWholeInputRegion; + /** + * @brief Select single input image in a timeseries + */ + mitk::ImageTimeSelector::Pointer m_InputTimeSelector; + /** + * @brief Select single output image in a timeseries + */ + mitk::ImageTimeSelector::Pointer m_OutputTimeSelector; + /** + * @brief Region of input needed for cutting + */ + typedef itk::ImageRegion<5> RegionType; + + mitk::SlicedData::RegionType m_InputRequestedRegion; + /** + * @brief Time when Header was last initialized + **/ + itk::TimeStamp m_TimeOfHeaderInitialization; + + }; +} // namespace mitk + +#endif /* BOUNDINGSHAPECROPPER_H */ diff --git a/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h b/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h new file mode 100644 index 0000000000..ae65e694bb --- /dev/null +++ b/Modules/BoundingShape/include/mitkBoundingShapeInteractor.h @@ -0,0 +1,142 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkBoundingShapeInteractor_h +#define mitkBoundingShapeInteractor_h + +#include +#include +#include + +#include + +#include + +namespace mitk +{ + +//create events for interactions +#pragma GCC visibility push(default) +itkEventMacro(BoundingShapeInteractionEvent, itk::AnyEvent) +#pragma GCC visibility pop + +/** + * @brief Basic interaction methods for mitk::GeometryData + * + * Inherit from DataInteratcor, this provides functionality of a state machine and configurable inputs. + * + * \ingroup Interaction + */ +class MITKBOUNDINGSHAPE_EXPORT BoundingShapeInteractor : public DataInteractor +{ +public: + mitkClassMacro(BoundingShapeInteractor, DataInteractor); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + virtual void SetDataNode(DataNode* dataNode) override; + void SetRotationEnabled(bool rotationEnabled); + +protected: + BoundingShapeInteractor(); + virtual ~BoundingShapeInteractor(); + + /** + * Here actions strings from the loaded state machine pattern are mapped to functions of + * the DataInteractor. These functions are called when an action from the state machine pattern is executed. + */ + virtual void ConnectActionsAndFunctions() override; + + /** + * @brief Called when a DataNode has been set/changed. + */ + virtual void DataNodeChanged() override; + + void HandlePositionChanged(const InteractionEvent* interactionEvent, Point3D ¢er); + + /** + * @brief Checks if the mouse pointer is over the object. + */ + virtual bool CheckOverObject (const InteractionEvent*); + + /** + * @brief Checks if the mouse pointer is over one of the assigned handles. + */ + virtual bool CheckOverHandles(const InteractionEvent* interactionEvent); + + /** + * @brief Called if the mouse pointer is over the object indicated by a color change + */ + virtual void SelectObject (StateMachineAction*, InteractionEvent*); + + /** + * @brief Called if the mouse pointer leaves the area of the object + */ + virtual void DeselectObject (StateMachineAction*, InteractionEvent*); + + /** + * @brief Called if the mouse pointer is over one of the handles indicated by a color change + */ + virtual void SelectHandle(StateMachineAction*, InteractionEvent*); + + /** + * @brief Performs a translation of the object relative to the mouse movement + */ + virtual void TranslateObject (StateMachineAction*, InteractionEvent*); + + /** + * @brief Performs a object shape change by influencing the scaling of the initial bounding box + */ + virtual void ScaleObject(StateMachineAction*, InteractionEvent*); + + /** + * @brief Performs a rotation of the object relative to the mouse movement + */ + //virtual void RotateObject(StateMachineAction*, InteractionEvent* interactionEvent); /* not implemented */ + + /** + * @brief Initializes the movement, stores starting position + */ + virtual void InitInteraction(StateMachineAction*, InteractionEvent* interactionEvent); + /** + * @brief Deselects all Handles at the end of interaction + */ + virtual void DeselectHandles(StateMachineAction*, InteractionEvent* interactionEvent); + /** + * @brief Restore default properties of bounding box and handles + */ + virtual void RestoreNodeProperties(); + /** + * @brief Initializes member variables. + */ + bool InitMembers(InteractionEvent* interactionEvent); + +private: + /** + * @brief Enables default crosshair properties + */ + void EnableCrosshairNavigation(); + /** + * @brief Sets limited crosshair properties (disable crosshair movement) + */ + void DisableCrosshairNavigation(); + + class Impl; + Impl *m_Impl; +}; + +} +#endif diff --git a/Modules/BoundingShape/include/mitkBoundingShapeObjectFactory.h b/Modules/BoundingShape/include/mitkBoundingShapeObjectFactory.h new file mode 100644 index 0000000000..ed1a532c80 --- /dev/null +++ b/Modules/BoundingShape/include/mitkBoundingShapeObjectFactory.h @@ -0,0 +1,55 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkBoundingShapeObjectFactory_h +#define mitkBoundingShapeObjectFactory_h + +#include +#include + +namespace mitk +{ + class MITKBOUNDINGSHAPE_EXPORT BoundingShapeObjectFactory : public CoreObjectFactoryBase + { + public: + + mitkClassMacro(BoundingShapeObjectFactory, CoreObjectFactoryBase) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + virtual Mapper::Pointer CreateMapper(mitk::DataNode* node, MapperSlotId slotId) override; + virtual void SetDefaultProperties(mitk::DataNode* node) override; + virtual const char* GetFileExtensions() override; + virtual mitk::CoreObjectFactoryBase::MultimapType GetFileExtensionsMap() override; + virtual const char* GetSaveFileExtensions() override; + virtual mitk::CoreObjectFactoryBase::MultimapType GetSaveFileExtensionsMap() override; + virtual const char* mitk::BoundingShapeObjectFactory::GetDescription() const override; + + protected: + BoundingShapeObjectFactory(); + virtual ~BoundingShapeObjectFactory(); + + void CreateFileExtensionsMap(); + + private: + MultimapType m_FileExtensionsMap; + MultimapType m_SaveFileExtensionsMap; + }; + + MITKBOUNDINGSHAPE_EXPORT void RegisterBoundingShapeObjectFactory(); +} + +#endif diff --git a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h new file mode 100644 index 0000000000..7fd1d03fec --- /dev/null +++ b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h @@ -0,0 +1,86 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkBoundingShapeVtkMapper2D_h +#define mitkBoundingShapeVtkMapper2D_h + +#include + +#include + +#include +#include +#include +#include +#include +#include +#include + +namespace mitk +{ + class MITKBOUNDINGSHAPE_EXPORT BoundingShapeVtkMapper2D final : public VtkMapper + { + class LocalStorage : public Mapper::BaseLocalStorage + { + public: + LocalStorage(); + ~LocalStorage(); + + bool IsUpdateRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode); + + vtkSmartPointer m_Actor; + vtkSmartPointer m_HandleActor; + vtkSmartPointer m_SelectedHandleActor; + vtkSmartPointer m_Mapper; + vtkSmartPointer m_HandleMapper; + vtkSmartPointer m_SelectedHandleMapper; + vtkSmartPointer m_Cutter; + vtkSmartPointer m_CuttingPlane; + std::vector> m_Handles; + vtkSmartPointer m_PropAssembly; + double m_ZoomFactor; + + private: + LocalStorage(const LocalStorage&); + LocalStorage& operator=(const LocalStorage&); + }; + + public: + static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool overwrite = false); + + mitkClassMacro(BoundingShapeVtkMapper2D, VtkMapper); + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + void ApplyColorAndOpacityProperties(BaseRenderer*, vtkActor*) override; + vtkProp* GetVtkProp(BaseRenderer* renderer) override; + + private: + BoundingShapeVtkMapper2D(); + ~BoundingShapeVtkMapper2D(); + + BoundingShapeVtkMapper2D(const Self&); + Self& operator=(const Self&); + + void GenerateDataForRenderer(BaseRenderer* renderer) override; + void Update(mitk::BaseRenderer* renderer) override; + + class Impl; + Impl* m_Impl; + }; +} + +#endif diff --git a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h new file mode 100644 index 0000000000..a47fd9fbd1 --- /dev/null +++ b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h @@ -0,0 +1,75 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + + +#ifndef mitkBoundingShapeVtkMapper3D_h +#define mitkBoundingShapeVtkMapper3D_h + +#include + +#include + +#include +#include +#include + +namespace mitk +{ + + class MITKBOUNDINGSHAPE_EXPORT BoundingShapeVtkMapper3D : public VtkMapper + { + class LocalStorage : public Mapper::BaseLocalStorage + { + public: + LocalStorage(); + ~LocalStorage(); + std::vector> m_Handles; + vtkSmartPointer m_Actor; + vtkSmartPointer m_HandleActor; + vtkSmartPointer m_SelectedHandleActor; + vtkSmartPointer m_PropAssembly; + private: + LocalStorage(const LocalStorage&); + LocalStorage& operator=(const LocalStorage&); + }; + + public: + static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = NULL, bool overwrite = false); + + mitkClassMacro(BoundingShapeVtkMapper3D, VtkMapper) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + void ApplyColorAndOpacityProperties(BaseRenderer*, vtkActor*) override; + void ApplyBoundingShapeProperties(BaseRenderer* renderer, vtkActor*); + vtkProp* GetVtkProp(BaseRenderer* renderer) override; + // virtual void UpdateVtkTransform(mitk::BaseRenderer* renderer) override; + protected: + void GenerateDataForRenderer(BaseRenderer* renderer) override; + + private: + BoundingShapeVtkMapper3D(); + ~BoundingShapeVtkMapper3D(); + + BoundingShapeVtkMapper3D(const Self&); + Self& operator=(const Self&); + + class Impl; + Impl* m_Impl; + }; +} + +#endif diff --git a/Modules/BoundingShape/resource/Interactions/BoundingShapeInteraction.xml b/Modules/BoundingShape/resource/Interactions/BoundingShapeInteraction.xml new file mode 100644 index 0000000000..af2dbfd914 --- /dev/null +++ b/Modules/BoundingShape/resource/Interactions/BoundingShapeInteraction.xml @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Modules/BoundingShape/resource/Interactions/BoundingShapeKeyConfig.xml b/Modules/BoundingShape/resource/Interactions/BoundingShapeKeyConfig.xml new file mode 100644 index 0000000000..e1ed120d9c --- /dev/null +++ b/Modules/BoundingShape/resource/Interactions/BoundingShapeKeyConfig.xml @@ -0,0 +1,58 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Modules/BoundingShape/resource/Interactions/BoundingShapeMouseConfig.xml b/Modules/BoundingShape/resource/Interactions/BoundingShapeMouseConfig.xml new file mode 100644 index 0000000000..3739d0c01b --- /dev/null +++ b/Modules/BoundingShape/resource/Interactions/BoundingShapeMouseConfig.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp new file mode 100644 index 0000000000..f31859d624 --- /dev/null +++ b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp @@ -0,0 +1,484 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkBoundingShapeCropper.h" +#include "mitkGeometry3D.h" +#include "mitkImageAccessByItk.h" +#include "mitkImageToItk.h" +#include "mitkImageCast.h" +#include "mitkStatusBar.h" +#include "mitkTimeHelper.h" + +#include + +#include "vtkMatrix4x4.h" +#include "vtkSmartPointer.h" +#include "vtkTransform.h" + +#include "itkImageRegionIteratorWithIndex.h" +#include +#include +#include +#include + + +namespace mitk +{ + BoundingShapeCropper::BoundingShapeCropper() + : m_Geometry(nullptr), + m_OutsideValue(0), + m_UseCropTimeStepOnly(false), + m_CurrentTimeStep(0), + m_UseWholeInputRegion(false), + m_InputTimeSelector(mitk::ImageTimeSelector::New()), + m_OutputTimeSelector(mitk::ImageTimeSelector::New()) + { + this->SetNumberOfIndexedInputs(2); + this->SetNumberOfRequiredInputs(2); + } + + BoundingShapeCropper::~BoundingShapeCropper() + { + } + + //template < typename TPixel, unsigned int VImageDimension, typename TOutputPixel > + //void BoundingShapeCropper::CutImage + // (itk::VectorImage* inputItkImage, int timeStep/* boTimeStep */, TOutputPixel* /* dummy */) + //{ + // // TO DO make possible to crop vector images + //} + template < typename TPixel, unsigned int VImageDimension > + void BoundingShapeCropper::CutImage(itk::Image< TPixel, VImageDimension >* inputItkImage, int timeStep) + { + MITK_INFO << "Scalar Pixeltype" << std::endl; + + typedef TPixel TOutputPixel; + typedef itk::Image ItkInputImageType; + typedef itk::Image ItkOutputImageType; + typedef typename itk::ImageBase::RegionType ItkRegionType; + typedef itk::ImageRegionIteratorWithIndex< ItkInputImageType > ItkInputImageIteratorType; + typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType; + + TOutputPixel outsideValue = this->GetOutsideValue(); + // currently 0 if not set in advance + // TODO: change default value to itk::NumericTraits::min(); + + if (this->m_Geometry.IsNull()) + return; + + if (inputItkImage == nullptr) + { + mitk::StatusBar::GetInstance()->DisplayErrorText("An internal error occurred. Can't convert Image. Please report to bugs@mitk.org"); + std::cout << " image is NULL...returning" << std::endl; + return; + } + + // first convert the index + typename ItkRegionType::IndexType::IndexValueType tmpIndex[3]; + itk2vtk(this->m_InputRequestedRegion.GetIndex(), tmpIndex); + typename ItkRegionType::IndexType index; + index.SetIndex(tmpIndex); + + // then convert the size + typename ItkRegionType::SizeType::SizeValueType tmpSize[3]; + itk2vtk(this->m_InputRequestedRegion.GetSize(), tmpSize); + typename ItkRegionType::SizeType size; + size.SetSize(tmpSize); + + //create the ITK-image-region out of index and size + ItkRegionType inputRegionOfInterest(index, size); + + + // Get access to the MITK output image via an ITK image + typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); + outputimagetoitk->SetInput(this->m_OutputTimeSelector->GetOutput()); + outputimagetoitk->Update(); + typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); + + // create the iterators + ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); + ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion()); + + // Cut the boundingbox out of the image by iterating through all images + // TODO: use more efficient method by using the contour instead off all single pixels + mitk::Point3D p; + mitk::BaseGeometry* inputGeometry = this->GetInput()->GetGeometry(timeStep); + + // calculates translation based on offset+extent not on the transformation matrix + // NOTE: center of the box is + vtkSmartPointer imageTransform = this->m_Geometry->GetGeometry()->GetVtkTransform()->GetMatrix(); + Point3D spacing = this->m_Geometry->GetGeometry()->GetSpacing(); + Point3D center = this->m_Geometry->GetGeometry()->GetCenter(); + auto translation = vtkSmartPointer::New(); + translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); + auto transform = vtkSmartPointer::New(); + transform->SetMatrix(imageTransform); + transform->PostMultiply(); + transform->Concatenate(translation); + transform->Update(); + + mitk::Vector3D extent; + for (unsigned int i = 0; i < 3; ++i) + extent[i] = (this->m_Geometry->GetGeometry()->GetExtent(i)); + + for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) + { + vtk2itk(inputIt.GetIndex(), p); + inputGeometry->IndexToWorld(p, p); + ScalarType p2[4]; + p2[0] = p[0]; + p2[1] = p[1]; + p2[2] = p[2]; + p2[3] = 1; + // transform point from world to object coordinates + transform->GetInverse()->TransformPoint(p2, p2); + // check if the world point is within bounds + bool isInside = (p2[0] >= (-extent[0] / 2.0)) && (p2[0] <= (extent[0] / 2.0)) + && (p2[1] >= (-extent[1] / 2.0)) && (p2[1] <= (extent[1] / 2.0)) + && (p2[2] >= (-extent[2] / 2.0)) && (p2[2] <= (extent[2] / 2.0)); + + if ((!this->m_UseCropTimeStepOnly && isInside) + || (this->m_UseCropTimeStepOnly && timeStep != this->m_CurrentTimeStep) + || (this->m_UseCropTimeStepOnly && timeStep == this->m_CurrentTimeStep && isInside)) + { + outputIt.Set((TOutputPixel)inputIt.Value()); + } + else + { + outputIt.Set(outsideValue); + } + } + } + + template < typename TPixel, unsigned int VImageDimension > + void BoundingShapeCropper::CutImage(itk::Image, VImageDimension >* inputItkImage, int timeStep) + { + MITK_INFO << "RGB Pixeltype" << std::endl; + + typedef TPixel TOutputPixel; + typedef itk::RGBPixel PixelType; + typedef itk::RGBPixel OutputPixelType; + typedef itk::Image ItkInputImageType; + typedef itk::Image ItkOutputImageType; + typedef typename itk::ImageBase::RegionType ItkRegionType; + typedef itk::ImageRegionIteratorWithIndex< ItkInputImageType > ItkInputImageIteratorType; + typedef itk::ImageRegionIteratorWithIndex< ItkOutputImageType > ItkOutputImageIteratorType; + + TOutputPixel outsideValue = itk::NumericTraits::min(); + if (this->m_Geometry.IsNull()) + return; + + if (inputItkImage == nullptr) + { + mitk::StatusBar::GetInstance()->DisplayErrorText("An internal error occurred. Can't convert Image. Please report to bugs@mitk.org"); + std::cout << " image is NULL...returning" << std::endl; + return; + } + + // first convert the index + typename ItkRegionType::IndexType::IndexValueType tmpIndex[3]; + itk2vtk(this->m_InputRequestedRegion.GetIndex(), tmpIndex); + typename ItkRegionType::IndexType index; + index.SetIndex(tmpIndex); + + // then convert the size + typename ItkRegionType::SizeType::SizeValueType tmpSize[3]; + itk2vtk(this->m_InputRequestedRegion.GetSize(), tmpSize); + typename ItkRegionType::SizeType size; + size.SetSize(tmpSize); + + //create the ITK-image-region out of index and size + ItkRegionType inputRegionOfInterest(index, size); + + // TODO maybe use mitkImageAccessor instead + typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); + outputimagetoitk->SetInput(this->m_OutputTimeSelector->GetOutput()); + outputimagetoitk->Update(); + typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); + + // create the iterators + ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); + ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion()); + + // Cut the boundingbox out of the image by iterating through + mitk::Point3D p; + + mitk::BaseGeometry* inputGeometry = this->GetInput()->GetGeometry(timeStep); + + // calculates translation based on offset+extent not on the transformation matrix + vtkSmartPointer imageTransform = this->m_Geometry->GetGeometry()->GetVtkTransform()->GetMatrix(); + Point3D center = this->m_Geometry->GetGeometry()->GetCenter(); + Point3D spacing = this->m_Geometry->GetGeometry()->GetSpacing(); + auto translation = vtkSmartPointer::New(); + translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); + + auto transform = vtkSmartPointer::New(); + transform->SetMatrix(imageTransform); + transform->PostMultiply(); + transform->Concatenate(translation); + transform->Update(); + + mitk::Vector3D extent; + for (unsigned int i = 0; i < 3; ++i) + extent[i] = (this->m_Geometry->GetGeometry()->GetExtent(i)); + + // no, use the pixel value of the original image (normal cutting) + for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) + { + vtk2itk(inputIt.GetIndex(), p); + inputGeometry->IndexToWorld(p, p); + ScalarType p2[4]; + p2[0] = p[0]; + p2[1] = p[1]; + p2[2] = p[2]; + p2[3] = 1; + // transform point from world to object coordinates + transform->GetInverse()->TransformPoint(p2, p2); + // check if the world point is within bounds + bool isInside = (p2[0] >= (-extent[0] / 2.0)) && (p2[0] <= (extent[0] / 2.0)) + && (p2[1] >= (-extent[1] / 2.0)) && (p2[1] <= (extent[1] / 2.0)) + && (p2[2] >= (-extent[2] / 2.0)) && (p2[2] <= (extent[2] / 2.0)); + + if ((!this->m_UseCropTimeStepOnly && isInside) + || (this->m_UseCropTimeStepOnly && timeStep != this->m_CurrentTimeStep) + || (this->m_UseCropTimeStepOnly && timeStep == this->m_CurrentTimeStep && isInside)) + { + outputIt.Set((OutputPixelType)inputIt.Value()); + } + else + { + outputIt.Set(outsideValue); + } + } + } + + void BoundingShapeCropper::SetGeometry(const mitk::GeometryData* geometry) + { + m_Geometry = const_cast(geometry); + // Process object is not const-correct so the const_cast is required here + this->ProcessObject::SetNthInput(1, const_cast(geometry)); + } + + + //const mitk::GeometryData* BoundingShapeCropper::GetGeometryData() const + //{ + // return m_Geometry.GetPointer(); + //} + + const mitk::PixelType BoundingShapeCropper::GetOutputPixelType() + { + return this->GetInput()->GetPixelType(); + } + + void BoundingShapeCropper::GenerateInputRequestedRegion() + { + mitk::Image* output = this->GetOutput(); + if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) || (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0)) + return; + + GenerateTimeInInputRegion(output, const_cast (this->GetInput())); + } + + void BoundingShapeCropper::GenerateOutputInformation() + { + + // Set Cropping region + mitk::Image::Pointer output = this->GetOutput(); + if ((output->IsInitialized()) && (output->GetPipelineMTime() <= m_TimeOfHeaderInitialization.GetMTime())) + return; + + mitk::Image::Pointer input = const_cast (this->GetInput()); + + if (input.IsNull()) + { + mitkThrow() << "Input is not a mitk::Image"; + } + itkDebugMacro(<< "GenerateOutputInformation()"); + + unsigned int dimension = input->GetDimension(); + if (dimension < 3) + { + mitkThrow() << "ImageCropper cannot handle 1D or 2D Objects."; + } + + if ((m_Geometry.IsNull()) || (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0)) + return; + + mitk::BaseGeometry* bsGeometry = m_Geometry->GetGeometry(); + mitk::BaseGeometry* inputImageGeometry = input->GetSlicedGeometry(); + + //calculate bounding box + mitk::BoundingBox::Pointer bsBoxRelativeToImage = bsGeometry->CalculateBoundingBoxRelativeToTransform(inputImageGeometry->GetIndexToWorldTransform()); + + // pre-initialize input-requested-region to largest-possible-region + m_InputRequestedRegion = input->GetLargestPossibleRegion(); + // build region out of bounding-box of index and size of the bounding box + mitk::SlicedData::IndexType index = m_InputRequestedRegion.GetIndex(); //init times and channels + mitk::BoundingBox::PointType min = bsBoxRelativeToImage->GetMinimum(); + mitk::SlicedData::SizeType size = m_InputRequestedRegion.GetSize(); //init times and channels + mitk::BoundingBox::PointType max = bsBoxRelativeToImage->GetMaximum(); + + for (unsigned int i = 0; i < dimension; i++) + { + index[i] = (mitk::SlicedData::IndexType::IndexValueType)(std::ceil(min[i])); + size[i] = (mitk::SlicedData::SizeType::SizeValueType)(std::ceil(max[i]) - index[i]); + } + mitk::SlicedData::RegionType bsRegion(index, size); + + if (m_UseWholeInputRegion == false) + { + // crop input-requested-region with region of bounding-object + if (m_InputRequestedRegion.Crop(bsRegion) == false) + { + // crop not possible => do nothing: set time size to 0. + size.Fill(0); + m_InputRequestedRegion.SetSize(size); + bsRegion.SetSize(size); + mitkThrow() << "No overlap of the image and the cropping object."; + } + } + + //initialize output image + auto dimensions = new unsigned int[dimension]; + if (dimension > 3 && !this->GetUseCropTimeStepOnly()) + memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3)*sizeof(unsigned int)); + else + dimension = 3; // set timeStep to zero if GetUseCropTimeStepOnly is true + + itk2vtk(m_InputRequestedRegion.GetSize(), dimensions); + + output->Initialize(mitk::PixelType(GetOutputPixelType()), dimension, dimensions); + delete[] dimensions; + + // Apply transform of the input image to the new generated output image + mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion(); + const mitk::TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); + + m_TimeOfHeaderInitialization.Modified(); + } + + void BoundingShapeCropper::ComputeData(mitk::Image* image, int boTimeStep) + { + // examine dimension and pixeltype + if ((image == nullptr) || (image->GetDimension() > 4) || (image->GetDimension() <= 2)) + { + MITK_ERROR << "Filter cannot handle dimensions less than 2 and greater than 4" << std::endl; + itkExceptionMacro("Filter cannot handle dimensions less than 2 and greater than 4"); + return; + } + + // Check if pixeltype is rgb + const mitk::PixelType &pixelType = image->GetPixelType(); + + if ((pixelType.GetPixelType() == itk::ImageIOBase::RGB) || (pixelType.GetPixelType() == itk::ImageIOBase::RGBA)) + { + MITK_INFO << "is rgb"; + switch (pixelType.GetComponentType()) + { + case itk::ImageIOBase::UCHAR: + { + AccessFixedPixelTypeByItk_1(image, CutImage, (itk::RGBPixel), boTimeStep); + } + case itk::ImageIOBase::USHORT: + { + AccessFixedPixelTypeByItk_1(image, CutImage, (itk::RGBPixel), boTimeStep); + break; + } + case itk::ImageIOBase::FLOAT: + { + AccessFixedPixelTypeByItk_1(image, CutImage, (itk::RGBPixel), boTimeStep); + break; + } + case itk::ImageIOBase::DOUBLE: + { + AccessFixedPixelTypeByItk_1(image, CutImage, (itk::RGBPixel), boTimeStep); + break; + } + default: + MITK_ERROR << "Can not handle pixel component type " << pixelType.GetComponentType(); + return; + } + } + else if (pixelType.GetPixelType() == itk::ImageIOBase::SCALAR) + { + switch (image->GetDimension()) + { + case 2: + { + AccessFixedDimensionByItk_1(image, CutImage, 2, boTimeStep); break; + } + case 3: + { + AccessFixedDimensionByItk_1(image, CutImage, 3, boTimeStep); break; + } + case 4: + { + AccessFixedDimensionByItk_1(image, CutImage, 4, boTimeStep); break; + } + default: break; + } + } + else + { + MITK_ERROR << "Unsupported pixel type" << std::endl; + } + } + + void BoundingShapeCropper::GenerateData() + { + MITK_INFO << "Generate Data" << std::endl; + mitk::Image::ConstPointer input = this->GetInput(); + mitk::Image::Pointer output = this->GetOutput(); + + if (input.IsNull()) + return; + + if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) || (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0)) + return; + + m_InputTimeSelector->SetInput(input); + m_OutputTimeSelector->SetInput(this->GetOutput()); + + mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion(); + mitk::BaseGeometry* inputImageGeometry = input->GetSlicedGeometry(); + + // iterate over all time steps and perform cropping or masking on all or a specific timestep (perviously specified by UseCurrentTimeStepOnly) + int tstart = outputRegion.GetIndex(3); + int tmax = tstart + outputRegion.GetSize(3); + int t; + for (t = tstart; t < tmax; ++t) + { + mitk::SlicedGeometry3D* slicedGeometry = output->GetSlicedGeometry(t); + auto indexToWorldTransform = AffineTransform3D::New(); + indexToWorldTransform->SetParameters(input->GetSlicedGeometry(t)->GetIndexToWorldTransform()->GetParameters()); + slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform); + + const mitk::SlicedData::IndexType& start = m_InputRequestedRegion.GetIndex(); + mitk::Point3D origin; vtk2itk(start, origin); + inputImageGeometry->IndexToWorld(origin, origin); + slicedGeometry->SetOrigin(origin); + m_InputTimeSelector->SetTimeNr(t); + m_InputTimeSelector->UpdateLargestPossibleRegion(); + m_OutputTimeSelector->SetTimeNr(t); + m_OutputTimeSelector->UpdateLargestPossibleRegion(); + ComputeData(m_InputTimeSelector->GetOutput(), t); + } + m_InputTimeSelector->SetInput(nullptr); + m_OutputTimeSelector->SetInput(nullptr); + m_TimeOfHeaderInitialization.Modified(); + } + +} // of namespace mitk \ No newline at end of file diff --git a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeObjectFactory.cpp b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeObjectFactory.cpp new file mode 100644 index 0000000000..7867d9e380 --- /dev/null +++ b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeObjectFactory.cpp @@ -0,0 +1,98 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkBoundingShapeObjectFactory.h" +#include "mitkBoundingShapeVtkMapper2D.h" +#include "mitkBoundingShapeVtkMapper3D.h" +#include + +mitk::BoundingShapeObjectFactory::BoundingShapeObjectFactory() +{ +} + +mitk::BoundingShapeObjectFactory::~BoundingShapeObjectFactory() +{ +} + +mitk::Mapper::Pointer mitk::BoundingShapeObjectFactory::CreateMapper(mitk::DataNode* node, MapperSlotId slotId) +{ + Mapper::Pointer mapper; + + if (dynamic_cast(node->GetData()) != NULL) + { + if (slotId == BaseRenderer::Standard2D) + { + mapper = BoundingShapeVtkMapper2D::New(); + } + else if (slotId == BaseRenderer::Standard3D) + { + mapper = BoundingShapeVtkMapper3D::New(); + } + + if (mapper.IsNotNull()) + mapper->SetDataNode(node); + } + + return mapper; +} + +const char* mitk::BoundingShapeObjectFactory::GetDescription() const +{ + return "BoundingShape Object Factory"; +} + +const char* mitk::BoundingShapeObjectFactory::GetFileExtensions() +{ + return nullptr; +} + +mitk::CoreObjectFactoryBase::MultimapType mitk::BoundingShapeObjectFactory::GetFileExtensionsMap() +{ + return MultimapType(); +} + +const char* mitk::BoundingShapeObjectFactory::GetSaveFileExtensions() +{ + return nullptr; +} + +mitk::CoreObjectFactoryBase::MultimapType mitk::BoundingShapeObjectFactory::GetSaveFileExtensionsMap() +{ + return MultimapType(); +} + +void mitk::BoundingShapeObjectFactory::SetDefaultProperties(mitk::DataNode* node) +{ + if (node == nullptr) + return; + + if (dynamic_cast(node->GetData()) != nullptr) + { + BoundingShapeVtkMapper2D::SetDefaultProperties(node); + BoundingShapeVtkMapper3D::SetDefaultProperties(node); + } +} + +void mitk::RegisterBoundingShapeObjectFactory() +{ + static bool alreadyRegistered = false; + + if (!alreadyRegistered) + { + CoreObjectFactory::GetInstance()->RegisterExtraFactory(BoundingShapeObjectFactory::New()); + alreadyRegistered = true; + } +} diff --git a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.cpp b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.cpp new file mode 100644 index 0000000000..f5cbd7073f --- /dev/null +++ b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.cpp @@ -0,0 +1,175 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "mitkBoundingShapeUtil.h" +#include "mitkGeometry3D.h" + +#include +#include "vtkMath.h" +#include "vtkDoubleArray.h" + +#include + +mitk::Handle::Handle() + : m_IsActive(false), + m_FaceIndices(4) +{ + m_Position.Fill(0.0); +} + +mitk::Handle::Handle(mitk::Point3D pos, int index, std::vector faceIndices, bool active) + : m_IsActive(active), + m_Position(pos), + m_FaceIndices(faceIndices), + m_Index(index) +{ +} + +mitk::Handle::~Handle() +{ +} + +bool mitk::Handle::IsActive(){ return m_IsActive; } +bool mitk::Handle::IsNotActive() { return !m_IsActive; }; +void mitk::Handle::SetActive(bool status) { m_IsActive = status; }; +void mitk::Handle::SetPosition(mitk::Point3D pos) { m_Position = pos; }; +mitk::Point3D mitk::Handle::GetPosition() { return m_Position; }; +void mitk::Handle::SetIndex(int index){ m_Index = index; }; +int mitk::Handle::GetIndex(){ return m_Index; }; +std::vector mitk::Handle::GetFaceIndices(){ return m_FaceIndices; }; + +mitk::Point3D mitk::CalcAvgPoint(mitk::Point3D a, mitk::Point3D b) +{ + mitk::Point3D c; + c[0] = (a[0] + b[0]) / 2.0; + c[1] = (a[1] + b[1]) / 2.0; + c[2] = (a[2] + b[2]) / 2.0; + return c; +} + +std::vector mitk::GetCornerPoints(mitk::BaseGeometry::Pointer geometry, bool visualizationOffset) +{ + if (geometry == nullptr) + mitkThrow() << "Geometry is not valid."; + + mitk::BoundingBox::ConstPointer boundingBox = geometry->GetBoundingBox(); + mitk::Point3D BBmin = boundingBox->GetMinimum(); + mitk::Point3D BBmax = boundingBox->GetMaximum(); + + // use 0.5 offset because the vtkCubeSource is not center pixel based (only for visualization purpose) + if (visualizationOffset) + { + BBmin -= 0.5; + BBmax -= 0.5; + } + mitk::Point3D p0; p0[0] = BBmin[0]; p0[1] = BBmin[1]; p0[2] = BBmin[2]; // bottom - left - back corner + mitk::Point3D p1; p1[0] = BBmin[0]; p1[1] = BBmin[1]; p1[2] = BBmax[2]; // top - left - back corner + mitk::Point3D p2; p2[0] = BBmin[0]; p2[1] = BBmax[1]; p2[2] = BBmin[2]; // bottom - left - front corner + mitk::Point3D p3; p3[0] = BBmin[0]; p3[1] = BBmax[1]; p3[2] = BBmax[2]; // top - left - front corner + mitk::Point3D p4; p4[0] = BBmax[0]; p4[1] = BBmin[1]; p4[2] = BBmin[2]; // bottom - right - back corner + mitk::Point3D p5; p5[0] = BBmax[0]; p5[1] = BBmin[1]; p5[2] = BBmax[2]; // top - right - back corner + mitk::Point3D p6; p6[0] = BBmax[0]; p6[1] = BBmax[1]; p6[2] = BBmin[2]; // bottom - right - front corner + mitk::Point3D p7; p7[0] = BBmax[0]; p7[1] = BBmax[1]; p7[2] = BBmax[2]; // top - right - front corner + + std::vector cornerPoints; + + cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p0)); + cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p1)); + cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p2)); + cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p3)); + cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p4)); + cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p5)); + cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p6)); + cornerPoints.push_back(geometry->GetIndexToWorldTransform()->TransformPoint(p7)); + + return cornerPoints; +} + +std::vector mitk::GetHandleIndices(int index) +{ + + std::vector faceIndices; + faceIndices.resize(4); + + // +------+ + // / /| + // +------+ | + // | | + + // | |/ + // +------+ + + switch (index) + { + case 0: + { + faceIndices[0] = 0; + faceIndices[1] = 1; + faceIndices[2] = 2; + faceIndices[3] = 3; + } + break; + case 1: + { + faceIndices[0] = 4; + faceIndices[1] = 5; + faceIndices[2] = 6; + faceIndices[3] = 7; + } + break; + case 3: + { + faceIndices[0] = 0; + faceIndices[1] = 2; + faceIndices[2] = 4; + faceIndices[3] = 6; + } + break; + case 2: + { + faceIndices[0] = 1; + faceIndices[1] = 3; + faceIndices[2] = 5; + faceIndices[3] = 7; + } + break; + case 4: + { + faceIndices[0] = 0; + faceIndices[1] = 1; + faceIndices[2] = 4; + faceIndices[3] = 5; + } + break; + case 5: + { + faceIndices[0] = 2; + faceIndices[1] = 3; + faceIndices[2] = 6; + faceIndices[3] = 7; + } + break; + default: + { + faceIndices[0] = 0; + faceIndices[1] = 0; + faceIndices[2] = 0; + faceIndices[3] = 0; + } + break; + } + + return faceIndices; +} \ No newline at end of file diff --git a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.h b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.h new file mode 100644 index 0000000000..58db65cf08 --- /dev/null +++ b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeUtil.h @@ -0,0 +1,69 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#ifndef mitkBoundingShapeUtil_h +#define mitkBoundingShapeUtil_h + +#include +#include +#include + + + +namespace mitk +{ + /** + * \brief helper function for calculating corner points of the bounding object from a given geometry + */ + std::vector GetCornerPoints(mitk::BaseGeometry::Pointer geometry, bool visualizationOffset); + + /** + * \brief helper function for calculating the average of two points + */ + mitk::Point3D CalcAvgPoint(mitk::Point3D a, mitk::Point3D b); + + std::vector GetHandleIndices(int index); + + /** + * \brief Helper Class for realizing the handles of bounding object encapsulated by a geometry data + * \ingroup Data + */ + class Handle final + { + public: + Handle(); + Handle(mitk::Point3D pos, int index, std::vector faceIndices, bool active = false); + + ~Handle(); + + bool IsActive(); + bool IsNotActive(); + void SetActive(bool status); + void SetIndex(int index); + int GetIndex(); + std::vector GetFaceIndices(); + void SetPosition(mitk::Point3D pos); + mitk::Point3D GetPosition(); + + private: + bool m_IsActive; + mitk::Point3D m_Position; + std::vector m_FaceIndices; + int m_Index; + }; +} + +#endif diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp new file mode 100644 index 0000000000..5a6ba9dd31 --- /dev/null +++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp @@ -0,0 +1,610 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include +#include +#include "../DataManagement/mitkBoundingShapeUtil.h" +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "usModuleRegistry.h" +#include "usGetModuleContext.h" + +// Properties to allow the user to interact with the base data +const char* selectedColorPropertyName = "BoundingShapeInteractor.selected color"; +const char* deselectedColorPropertyName = "BoundingShapeInteractor.deselected color"; +const char* activeHandleIdPropertyName = "BoundingShapeInteractor.active handle id"; +const char* boundingShapePropertyName = "BoundingShapeInteractor"; + +namespace mitk +{ + class BoundingShapeInteractor::Impl + { + public: + Impl() + : ScrollEnabled(false), + RotationEnabled(false) + { + Point3D initialPoint; + initialPoint.Fill(0.0); + + for (int i = 0; i < 6; ++i) + Handles.push_back(Handle(initialPoint, i, GetHandleIndices(i))); + } + + ~Impl() + { + } + + bool ScrollEnabled; + Point3D InitialPickedWorldPoint; + Point3D LastPickedWorldPoint; + Point2D InitialPickedDisplayPoint; + std::vector Handles; + Handle ActiveHandle; + Geometry3D::Pointer OriginalGeometry; + bool RotationEnabled; + std::map DisplayInteractorConfigs; + }; +} + +mitk::BoundingShapeInteractor::BoundingShapeInteractor() + : m_Impl(new Impl) +{ +} + +mitk::BoundingShapeInteractor::~BoundingShapeInteractor() +{ + this->RestoreNodeProperties(); + delete m_Impl; +} + +void mitk::BoundingShapeInteractor::ConnectActionsAndFunctions() +{ + // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually executing an action + CONNECT_CONDITION("isHoveringOverObject", CheckOverObject); + CONNECT_CONDITION("isHoveringOverHandles", CheckOverHandles); + + // **Function** in the statemachine patterns also referred to as **Actions** + CONNECT_FUNCTION("selectObject", SelectObject); + CONNECT_FUNCTION("deselectObject", DeselectObject); + CONNECT_FUNCTION("deselectHandles", DeselectHandles); + CONNECT_FUNCTION("initInteraction", InitInteraction); + CONNECT_FUNCTION("translateObject", TranslateObject); + CONNECT_FUNCTION("selectHandle", SelectHandle); + CONNECT_FUNCTION("scaleObject", ScaleObject); + //CONNECT_FUNCTION("rotateObject",RotateObject); + +} + + +//RotateObject(StateMachineAction*, InteractionEvent* interactionEvent) +//void mitk::BoundingShapeInteractor::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry* geometry) +//{ +// mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis); +// float pointX = 0.0f; +// float pointY = 0.0f; +// float pointZ = 0.0f; +// mitk::Point3D pointOfRotation; +// pointOfRotation.Fill(0.0); +// this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); +// this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); +// this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); +// pointOfRotation[0] = pointX; +// pointOfRotation[1] = pointY; +// pointOfRotation[2] = pointZ; +// +// mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); +// +// geometry->ExecuteOperation(doOp); +// delete doOp; +//} + + +void mitk::BoundingShapeInteractor::SetRotationEnabled(bool rotationEnabled) +{ + m_Impl->RotationEnabled = rotationEnabled; +} + +void mitk::BoundingShapeInteractor::DataNodeChanged() +{ + mitk::DataNode::Pointer newInputNode = this->GetDataNode(); + + if (newInputNode == nullptr) + return; + + //add color properties + mitk::ColorProperty::Pointer selectedColor = dynamic_cast(newInputNode->GetProperty(selectedColorPropertyName)); + mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); + + if (selectedColor.IsNull()) + newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0)); + + if (deselectedColor.IsNull()) + newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 1.0, 1.0)); + + newInputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(true)); + newInputNode->AddProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); + newInputNode->SetProperty("layer", mitk::IntProperty::New(101)); + newInputNode->SetBoolProperty("fixedLayer", mitk::BoolProperty::New(true)); + newInputNode->SetBoolProperty("pickable", true); + + mitk::ColorProperty::Pointer initialColor = dynamic_cast(newInputNode->GetProperty(deselectedColorPropertyName)); + if (initialColor.IsNotNull()) + { + newInputNode->SetColor(initialColor->GetColor()); + } + + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void mitk::BoundingShapeInteractor::HandlePositionChanged(const InteractionEvent* interactionEvent, Point3D ¢er) +{ + GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); + int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); + mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); + + std::vector cornerPoints = GetCornerPoints(geometry, true); + if (m_Impl->Handles.size() == 6){ + // set handle positions + Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); + Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); + Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); + Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); + Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); + Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); + + m_Impl->Handles[0].SetPosition(pointLeft); + m_Impl->Handles[1].SetPosition(pointRight); + m_Impl->Handles[2].SetPosition(pointTop); + m_Impl->Handles[3].SetPosition(pointBottom); + m_Impl->Handles[4].SetPosition(pointFront); + m_Impl->Handles[5].SetPosition(pointBack); + + // calculate center based on half way of the distance between two opposing cornerpoints + center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); + } + + +} + +void mitk::BoundingShapeInteractor::SetDataNode(DataNode* node) +{ + this->RestoreNodeProperties(); //if there is another node set, restore it's color + + if (node == nullptr) + return; + DataInteractor::SetDataNode(node); // calls DataNodeChanged internally + this->DataNodeChanged(); +} + +bool mitk::BoundingShapeInteractor::CheckOverObject(const InteractionEvent* interactionEvent) +{ + const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); + if (positionEvent == nullptr) + return false; + + GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); + int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); + BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); + + // calculates translation based on offset+extent not on the transformation matrix (because the cube is located in the center not in the origin) + vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); + Point3D spacing = geometry->GetSpacing(); + Point3D center = geometry->GetCenter(); + auto translation = vtkSmartPointer::New(); + auto transform = vtkSmartPointer::New(); + translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); + transform->SetMatrix(imageTransform); + transform->PostMultiply(); + transform->Concatenate(translation); + transform->Update(); + + mitk::Vector3D extent; + for (unsigned int i = 0; i < 3; ++i) + extent[i] = (geometry->GetExtent(i)); + + Point3D currentWorldPosition; + Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); + interactionEvent->GetSender()->DisplayToWorld(currentDisplayPosition, currentWorldPosition); + + ScalarType transformedPosition[4]; + transformedPosition[0] = currentWorldPosition[0]; + transformedPosition[1] = currentWorldPosition[1]; + transformedPosition[2] = currentWorldPosition[2]; + transformedPosition[3] = 1; + // transform point from world to object coordinates + transform->GetInverse()->TransformPoint(transformedPosition, transformedPosition); + // check if the world point is within bounds + bool isInside = (transformedPosition[0] >= (-extent[0] / 2.0)) && (transformedPosition[0] <= (extent[0] / 2.0)) + && (transformedPosition[1] >= (-extent[1] / 2.0)) && (transformedPosition[1] <= (extent[1] / 2.0)) + && (transformedPosition[2] >= (-extent[2] / 2.0)) && (transformedPosition[2] <= (extent[2] / 2.0)); + + return isInside; +} + +bool mitk::BoundingShapeInteractor::CheckOverHandles(const InteractionEvent* interactionEvent) +{ + Point3D boundingBoxCenter; + HandlePositionChanged(interactionEvent, boundingBoxCenter); + const InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); + if (positionEvent == nullptr) + return false; + + Point2D displayCenterPoint; + // to do: change to actual time step (currently not necessary because geometry remains the same for each timestep + int timeStep = 0; + + GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); + BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); + std::vector cornerPoints = GetCornerPoints(geometry, true); + Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); + interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint); + double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); //GetDisplaySizeInMM + mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("handle size factor")); + + ScalarType initialHandleSize; + if (handleSizeProperty != nullptr) + initialHandleSize = handleSizeProperty->GetValue(); + else + initialHandleSize = 1.0 / 40.0; + + mitk::Point2D displaysize = interactionEvent->GetSender()->GetDisplaySizeInMM(); + ScalarType handlesize = ((displaysize[0] + displaysize[1]) / 2.0)*initialHandleSize; + Vector3D spacing = geometry->GetSpacing(); + unsigned int handleNum = 0; + + for (auto &handle : m_Impl->Handles) + { + Point2D centerpoint; + interactionEvent->GetSender()->WorldToDisplay(handle.GetPosition(), centerpoint); + Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); + + if ((currentDisplayPosition.EuclideanDistanceTo(centerpoint) < (handlesize / scale)) + && (currentDisplayPosition.EuclideanDistanceTo(displayCenterPoint) > (handlesize / scale))) // check if mouse is hovering over center point + { + handle.SetActive(true); + m_Impl->ActiveHandle = handle; + this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(handleNum++)); + this->GetDataNode()->GetData()->Modified(); + interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); + return true; + } + else + { + handleNum++; + handle.SetActive(false); + } + this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); + } + + return false; +} + +void mitk::BoundingShapeInteractor::SelectHandle(StateMachineAction*, InteractionEvent* interactionEvent) +{ + this->DisableCrosshairNavigation(); + DataNode::Pointer node = this->GetDataNode(); + + if (node.IsNull()) + return; + + mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); + if (selectedColor.IsNotNull()) + { + this->GetDataNode()->GetPropertyList()->SetProperty("color", selectedColor); + } + this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date + this->GetDataNode()->GetData()->Modified(); + interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); + return; +} + +void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction*, InteractionEvent* interactionEvent) +{ + this->DisableCrosshairNavigation(); + DataNode::Pointer node = this->GetDataNode(); + + if (node.IsNull()) + return; + + this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); + this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date + this->GetDataNode()->GetData()->Modified(); + interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); + + return; +} + + + +void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction*, InteractionEvent* interactionEvent) +{ + this->DisableCrosshairNavigation(); //disable crosshair interaction and scolling if user is hovering over the object + DataNode::Pointer node = this->GetDataNode(); + + if (node.IsNull()) + return; + + mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty(selectedColorPropertyName)); + if (selectedColor.IsNotNull()) + { + node->GetPropertyList()->SetProperty("color", selectedColor); + } + this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date + this->GetDataNode()->GetData()->Modified(); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + return; +} + +void mitk::BoundingShapeInteractor::DeselectObject(StateMachineAction*, InteractionEvent* interactionEvent) +{ + this->EnableCrosshairNavigation(); //enable crosshair interaction and scolling if user is hovering over the object + + DataNode::Pointer node = this->GetDataNode(); + + if (node.IsNull()) + return; + + mitk::ColorProperty::Pointer deselectedColor = dynamic_cast(node->GetProperty(deselectedColorPropertyName)); + if (deselectedColor.IsNotNull()) + { + node->GetPropertyList()->SetProperty("color", deselectedColor); + } + + this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date + this->GetDataNode()->GetData()->Modified(); + interactionEvent->GetSender()->GetRenderingManager()->RequestUpdateAll(); + return; +} + +void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction*, InteractionEvent* interactionEvent) +{ + InitMembers(interactionEvent); +} + + +bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent* interactionEvent) +{ + InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); + if (positionEvent == nullptr) + return false; + + //get initial position coordinates + m_Impl->InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); + m_Impl->InitialPickedWorldPoint = positionEvent->GetPositionInWorld(); + m_Impl->LastPickedWorldPoint = positionEvent->GetPositionInWorld(); + + return true; +} + +void mitk::BoundingShapeInteractor::TranslateObject(StateMachineAction*, InteractionEvent* interactionEvent) +{ + InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); + if (positionEvent == nullptr) + return; + + int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); + mitk::BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); + Vector3D spacing = geometry->GetSpacing(); + Point3D currentPickedPoint; + interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); + Vector3D interactionMove; + + // pixel aligned shifting of the bounding box + interactionMove[0] = std::round((currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]) / spacing[0])* spacing[0]; + interactionMove[1] = std::round((currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]) / spacing[1])* spacing[1]; + interactionMove[2] = std::round((currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]) / spacing[2])* spacing[2]; + + if ((interactionMove[0] + interactionMove[1] + interactionMove[2]) != 0.0) // only update current position if a movement occured + { + m_Impl->LastPickedWorldPoint = currentPickedPoint; + + geometry->SetOrigin(geometry->GetOrigin() + interactionMove); + + this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date + this->GetDataNode()->GetData()->Modified(); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + return; +} + + +void mitk::BoundingShapeInteractor::ScaleObject(StateMachineAction*, InteractionEvent* interactionEvent) +{ + InteractionPositionEvent* positionEvent = dynamic_cast(interactionEvent); + if (positionEvent == nullptr) + return; + + GeometryData::Pointer geometryData = dynamic_cast(this->GetDataNode()->GetData()); + Point3D handlePickedPoint = m_Impl->ActiveHandle.GetPosition(); + Point3D currentPickedPoint; + interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); + int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); + mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); + Vector3D spacing = geometry->GetSpacing(); + + // pixel aligned bounding box + Vector3D interactionMove; + interactionMove[0] = (currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]); + interactionMove[1] = (currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]); + interactionMove[2] = (currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]); + + std::vector faces = m_Impl->ActiveHandle.GetFaceIndices(); + auto pointscontainer = mitk::BoundingBox::PointsContainer::New(); + + // calculate cornerpoints from geometry plus visualization offset + std::vector cornerPoints = GetCornerPoints(geometry, true); + unsigned int num = 0; + for (auto point : cornerPoints) + { + pointscontainer->InsertElement(num++, point); + } + + // calculate center based on half way of the distance between two opposing cornerpoints + mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); + + Vector3D faceNormal; + faceNormal[0] = handlePickedPoint[0] - center[0]; + faceNormal[1] = handlePickedPoint[1] - center[1]; + faceNormal[2] = handlePickedPoint[2] - center[2]; + Vector3D faceShift = ((faceNormal * interactionMove) / (faceNormal.GetNorm()*faceNormal.GetNorm())) *faceNormal; + + // calculate cornerpoints from geometry without visualization offset to update actual geometry + cornerPoints = GetCornerPoints(geometry, false); + num = 0; + for (auto point : cornerPoints) + { + pointscontainer->InsertElement(num++, point); + } + + bool positionChangeThreshold = true; + for (unsigned int numFaces = 0; numFaces < 8; numFaces++) // estimate the corresponding face and shift its assigned points + { + if ((numFaces != faces[0]) && (numFaces != faces[1]) && (numFaces != faces[2]) && (numFaces != faces[3])) + { + Point3D point = pointscontainer->GetElement(numFaces); + if (m_Impl->RotationEnabled) // apply if geometry is rotated at a pixel aligned shift is not possible + { + point[0] += faceShift[0]; + point[1] += faceShift[1]; + point[2] += faceShift[2]; + } + else // shift pixelwise + { + point[0] += std::round(faceShift[0] / spacing[0])*spacing[0]; + point[1] += std::round(faceShift[1] / spacing[1])*spacing[1]; + point[2] += std::round(faceShift[2] / spacing[2])*spacing[2]; + + } + + if (point == pointscontainer->GetElement(numFaces)) + positionChangeThreshold = false; + else + m_Impl->LastPickedWorldPoint = point; + + pointscontainer->InsertElement(numFaces, point); + } + } + + if (positionChangeThreshold) // update only if bounding box is shifted at least by one pixel + { + + auto inverse = mitk::AffineTransform3D::New(); + geometry->GetIndexToWorldTransform()->GetInverse(inverse); + for (unsigned int pointid = 0; pointid < 8; pointid++) + { + pointscontainer->InsertElement(pointid, inverse->TransformPoint(pointscontainer->GetElement(pointid))); + } + + auto bbox = mitk::BoundingBox::New(); + bbox->SetPoints(pointscontainer); + bbox->ComputeBoundingBox(); + mitk::Point3D BBmin = bbox->GetMinimum(); + mitk::Point3D BBmax = bbox->GetMaximum(); + if (abs(BBmin[0] - BBmax[0]) > 0.01 && abs(BBmin[1] - BBmax[1]) > 0.01 && abs(BBmin[2] - BBmax[2]) > 0.01) //TODO: check if the extent is greater than zero + { + + geometry->SetBounds(bbox->GetBounds()); + geometry->Modified(); + this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date + this->GetDataNode()->GetData()->Modified(); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + } + return; +} + +void mitk::BoundingShapeInteractor::RestoreNodeProperties() +{ + mitk::DataNode::Pointer inputNode = this->GetDataNode(); + if (inputNode.IsNull()) + return; + + mitk::ColorProperty::Pointer color = (mitk::ColorProperty::New(1.0, 1.0, 1.0)); + if (color.IsNotNull()) + { + inputNode->GetPropertyList()->SetProperty("color", color); + } + inputNode->SetProperty("layer", mitk::IntProperty::New(99)); + inputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(false)); + inputNode->GetPropertyList()->DeleteProperty(activeHandleIdPropertyName); + + EnableCrosshairNavigation(); + //update rendering + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); +} + +void mitk::BoundingShapeInteractor::EnableCrosshairNavigation() +{ + // enable the crosshair navigation + // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools + // in new interaction framework + for (std::map::iterator it = m_Impl->DisplayInteractorConfigs.begin(); + it != m_Impl->DisplayInteractorConfigs.end(); ++it) + { + if (it->first) + { + mitk::DisplayInteractor* displayInteractor = static_cast( + us::GetModuleContext()->GetService(it->first)); + if (displayInteractor != nullptr) + { + // here the regular configuration is loaded again + displayInteractor->SetEventConfig(it->second); + // MITK_INFO << "restore config"; + } + } + } + m_Impl->DisplayInteractorConfigs.clear(); + m_Impl->ScrollEnabled = true; +} + +void mitk::BoundingShapeInteractor::DisableCrosshairNavigation() +{ + // dont deactivate twice, else we will clutter the config list ... + if (m_Impl->ScrollEnabled == false) + return; + + // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts with tools + // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction will still be enabled + m_Impl->DisplayInteractorConfigs.clear(); + std::vector > listEventObserver = us::GetModuleContext()->GetServiceReferences(); + for (std::vector >::iterator it = listEventObserver.begin(); it != listEventObserver.end(); ++it) + { + mitk::DisplayInteractor* displayInteractor = dynamic_cast( + us::GetModuleContext()->GetService(*it)); + if (displayInteractor != nullptr) + { + // remember the original configuration + m_Impl->DisplayInteractorConfigs.insert(std::make_pair(*it, displayInteractor->GetEventConfig())); + // here the alternative configuration is loaded + displayInteractor->SetEventConfig("DisplayConfigMITKNoCrosshair.xml"); + // MITK_INFO << "change config"; + } + } + + m_Impl->ScrollEnabled = false; +} \ No newline at end of file diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp new file mode 100644 index 0000000000..062fbabf42 --- /dev/null +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp @@ -0,0 +1,467 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for detailocalStorage. + +===================================================================*/ + +#include +#include +#include "../DataManagement/mitkBoundingShapeUtil.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static vtkSmartPointer CreateHandle() +{ + auto handle = vtkSmartPointer::New(); + + handle->SetPhiResolution(8); + handle->SetThetaResolution(16); + + return handle; +} + +namespace mitk +{ + class BoundingShapeVtkMapper2D::Impl + { + public: + Impl() + { + Point3D initialPoint; + initialPoint.Fill(0); + + for (int i = 0; i < 6; ++i) + HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i))); + } + + std::vector HandlePropertyList; + LocalStorageHandler LocalStorageHandler; + }; +} + +mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage() + : m_Actor(vtkSmartPointer::New()), + m_HandleActor(vtkSmartPointer::New()), + m_SelectedHandleActor(vtkSmartPointer::New()), + m_Mapper(vtkSmartPointer::New()), + m_HandleMapper(vtkSmartPointer::New()), + m_SelectedHandleMapper(vtkSmartPointer::New()), + m_Cutter(vtkSmartPointer::New()), + m_CuttingPlane(vtkSmartPointer::New()), + m_PropAssembly(vtkSmartPointer::New()), + m_ZoomFactor(1.0) +{ + m_Actor->SetMapper(m_Mapper); + m_Actor->GetProperty()->SetOpacity(0.3); + m_Actor->VisibilityOn(); + + m_HandleActor->SetMapper(m_HandleMapper); + m_HandleActor->VisibilityOn(); + + m_SelectedHandleActor->VisibilityOn(); + m_SelectedHandleActor->GetProperty()->SetColor(0, 1.0, 0); + m_SelectedHandleActor->SetMapper(m_SelectedHandleMapper); + + vtkCoordinate *tcoord = vtkCoordinate::New(); + tcoord->SetCoordinateSystemToWorld(); + m_SelectedHandleMapper->SetTransformCoordinate(tcoord); + tcoord->Delete(); + + m_Cutter->SetCutFunction(m_CuttingPlane); + + for (int i = 0; i < 6; ++i) + m_Handles.push_back(CreateHandle()); + + m_PropAssembly->AddPart(m_Actor); + m_PropAssembly->AddPart(m_HandleActor); + m_PropAssembly->VisibilityOn(); +} + + +bool mitk::BoundingShapeVtkMapper2D::LocalStorage::IsUpdateRequired( + mitk::BaseRenderer *renderer, + mitk::Mapper *mapper, + mitk::DataNode *dataNode) +{ + const mitk::PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); + + if (m_LastGenerateDataTime < worldGeometry->GetMTime()) + return true; + + if (mapper && m_LastGenerateDataTime < mapper->GetMTime()) + return true; + + if (dataNode) + { + if (m_LastGenerateDataTime < dataNode->GetMTime()) + return true; + + mitk::BaseData * data = dataNode->GetData(); + + if (data && m_LastGenerateDataTime < data->GetMTime()) + return true; + } + + return false; +} + + +mitk::BoundingShapeVtkMapper2D::LocalStorage::~LocalStorage() +{ +} + +void mitk::BoundingShapeVtkMapper2D::Update(mitk::BaseRenderer* renderer) +{ + this->GenerateDataForRenderer(renderer); +} + +void mitk::BoundingShapeVtkMapper2D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool overwrite) +{ + if (node != nullptr) + { + node->AddProperty("BoundingShape.Visual.2D Rendering", BoolProperty::New(false), renderer, overwrite); + Superclass::SetDefaultProperties(node, renderer, overwrite); + } +} + +mitk::BoundingShapeVtkMapper2D::BoundingShapeVtkMapper2D() + : m_Impl(new Impl) +{ +} + +mitk::BoundingShapeVtkMapper2D::~BoundingShapeVtkMapper2D() +{ + delete m_Impl; +} + +void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) +{ + const DataNode::Pointer node = GetDataNode(); + if (node == nullptr) + return; + + LocalStorage* localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer); + + // either update if GeometryData was modified or if the zooming was performed + bool needGenerateData = localStorage->IsUpdateRequired(renderer,this,GetDataNode());// true; // localStorage->GetLastGenerateDataTime() < node->GetMTime() || localStorage->GetLastGenerateDataTime() < node->GetData()->GetMTime(); //localStorage->IsGenerateDataRequired(renderer, this, GetDataNode()); + + double scale = renderer->GetScaleFactorMMPerDisplayUnit(); + + if (abs(scale - localStorage->m_ZoomFactor) > 0.001) + { + localStorage->m_ZoomFactor = scale; + needGenerateData = true; + } + + if (needGenerateData) + { + bool visible = true; + GetDataNode()->GetVisibility(visible, renderer, "visible"); + + if (!visible) + { + localStorage->m_Actor->VisibilityOff(); + return; + } + GeometryData::Pointer shape = static_cast(node->GetData()); + if (shape == nullptr) + return; + + mitk::BaseGeometry::Pointer geometry = shape->GetGeometry(); + mitk::Vector3D spacing = geometry->GetSpacing(); + + // calculate cornerpoints and extent from geometry with visualization offset + std::vector cornerPoints = GetCornerPoints(geometry ,true); + Point3D p0 = cornerPoints[0]; + Point3D p1 = cornerPoints[1]; + Point3D p2 = cornerPoints[2]; + Point3D p4 = cornerPoints[4]; + Point3D extent; + extent[0] = sqrt((p0[0] - p4[0])*(p0[0] - p4[0]) + (p0[1] - p4[1])*(p0[1] - p4[1]) + (p0[2] - p4[2])*(p0[2] - p4[2])); + extent[1] = sqrt((p0[0] - p2[0])*(p0[0] - p2[0]) + (p0[1] - p2[1])*(p0[1] - p2[1]) + (p0[2] - p2[2])*(p0[2] - p2[2])); + extent[2] = sqrt((p0[0] - p1[0])*(p0[0] - p1[0]) + (p0[1] - p1[1])*(p0[1] - p1[1]) + (p0[2] - p1[2])*(p0[2] - p1[2])); + + // calculate center based on half way of the distance between two opposing cornerpoints + mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); + + if (m_Impl->HandlePropertyList.size() == 6){ + // set handle positions + Point3D pointLeft= CalcAvgPoint(cornerPoints[5], cornerPoints[6]); + Point3D pointRight= CalcAvgPoint(cornerPoints[1], cornerPoints[2]); + Point3D pointTop= CalcAvgPoint(cornerPoints[0], cornerPoints[6]); + Point3D pointBottom= CalcAvgPoint(cornerPoints[7], cornerPoints[1]); + Point3D pointFront= CalcAvgPoint(cornerPoints[2], cornerPoints[7]); + Point3D pointBack= CalcAvgPoint(cornerPoints[4], cornerPoints[1]); + + m_Impl->HandlePropertyList[0].SetPosition(pointLeft); + m_Impl->HandlePropertyList[1].SetPosition(pointRight); + m_Impl->HandlePropertyList[2].SetPosition(pointTop); + m_Impl->HandlePropertyList[3].SetPosition(pointBottom); + m_Impl->HandlePropertyList[4].SetPosition(pointFront); + m_Impl->HandlePropertyList[5].SetPosition(pointBack); + } + + // caculate face normals + double result0[3], result1[3], result2[3]; + double a[3], b[3]; + a[0] = (cornerPoints[5][0] - cornerPoints[6][0]); + a[1] = (cornerPoints[5][1] - cornerPoints[6][1]); + a[2] = (cornerPoints[5][2] - cornerPoints[6][2]); + + b[0] = (cornerPoints[5][0] - cornerPoints[4][0]); + b[1] = (cornerPoints[5][1] - cornerPoints[4][1]); + b[2] = (cornerPoints[5][2] - cornerPoints[4][2]); + + vtkMath::Cross(a, b, result0); + + a[0] = (cornerPoints[0][0] - cornerPoints[6][0]); + a[1] = (cornerPoints[0][1] - cornerPoints[6][1]); + a[2] = (cornerPoints[0][2] - cornerPoints[6][2]); + + b[0] = (cornerPoints[0][0] - cornerPoints[2][0]); + b[1] = (cornerPoints[0][1] - cornerPoints[2][1]); + b[2] = (cornerPoints[0][2] - cornerPoints[2][2]); + + vtkMath::Cross(a, b, result1); + + a[0] = (cornerPoints[2][0] - cornerPoints[7][0]); + a[1] = (cornerPoints[2][1] - cornerPoints[7][1]); + a[2] = (cornerPoints[2][2] - cornerPoints[7][2]); + + b[0] = (cornerPoints[2][0] - cornerPoints[6][0]); + b[1] = (cornerPoints[2][1] - cornerPoints[6][1]); + b[2] = (cornerPoints[2][2] - cornerPoints[6][2]); + + vtkMath::Cross(a, b, result2); + + vtkMath::Normalize(result0); + vtkMath::Normalize(result1); + vtkMath::Normalize(result2); + + // create cube for rendering bounding box + auto cube = vtkCubeSource::New(); + cube->SetXLength(extent[0] / spacing[0] ); + cube->SetYLength(extent[1] / spacing[1] ); + cube->SetZLength(extent[2] / spacing[2] ); + + // calculates translation based on offset+extent not on the transformation matrix + vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); + auto translation = vtkSmartPointer::New(); + translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); + + auto transform = vtkSmartPointer::New(); + transform->SetMatrix(imageTransform); + transform->PostMultiply(); + transform->Concatenate(translation); + transform->Update(); + cube->Update(); + + auto transformFilter = vtkSmartPointer::New(); + transformFilter->SetInputData(cube->GetOutput()); + transformFilter->SetTransform(transform); + transformFilter->Update(); + cube->Delete(); + + vtkSmartPointer polydata = transformFilter->GetPolyDataOutput(); + if (polydata == nullptr || (polydata->GetNumberOfPoints() < 1)) + { + localStorage->m_Actor->VisibilityOff(); + localStorage->m_HandleActor->VisibilityOff(); + localStorage->m_SelectedHandleActor->VisibilityOff(); + return; + } + + // estimate current image plane to decide whether the cube is visible or not + const PlaneGeometry* planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); + if ((planeGeometry == nullptr) || (!planeGeometry->IsValid()) || (!planeGeometry->HasReferenceGeometry())) + return; + + double origin[3]; + origin[0] = planeGeometry->GetOrigin()[0]; + origin[1] = planeGeometry->GetOrigin()[1]; + origin[2] = planeGeometry->GetOrigin()[2]; + + double normal[3]; + normal[0] = planeGeometry->GetNormal()[0]; + normal[1] = planeGeometry->GetNormal()[1]; + normal[2] = planeGeometry->GetNormal()[2]; + +// MITK_INFO << "normal1 " << normal[0] << " " << normal[1] << " " << normal[2]; + localStorage->m_CuttingPlane->SetOrigin(origin); + localStorage->m_CuttingPlane->SetNormal(normal); + + // add cube polydata to local storage + localStorage->m_Cutter->SetInputData(polydata); + localStorage->m_Cutter->SetGenerateCutScalars(1); + localStorage->m_Cutter->Update(); + + if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_HandleActor)) + localStorage->m_PropAssembly->RemovePart(localStorage->m_HandleActor); + if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_Actor)) + localStorage->m_PropAssembly->RemovePart(localStorage->m_Actor); + + + vtkCoordinate *tcoord = vtkCoordinate::New(); + tcoord->SetCoordinateSystemToWorld(); + localStorage->m_HandleMapper->SetTransformCoordinate(tcoord); + tcoord->Delete(); + + + if (localStorage->m_Cutter->GetOutput()->GetNumberOfPoints() > 0) // if plane is visible in the renderwindow + { + mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("handle size factor")); + + ScalarType initialHandleSize; + if (handleSizeProperty != nullptr) + initialHandleSize = handleSizeProperty->GetValue(); + else + initialHandleSize = 1.0/40.0; + + mitk::Point2D displaySize = renderer->GetDisplaySizeInMM(); + double handleSize = ((displaySize[0] + displaySize[1]) / 2.0)*initialHandleSize; + + + + auto appendPoly = vtkSmartPointer::New(); + unsigned int i = 0; + + // add handles and their assigned properties to the local storage + mitk::IntProperty::Pointer activeHandleId = dynamic_cast(node->GetProperty("BoundingShapeInteractor.active handle id")); + + bool visible = false; + bool selected = false; + for (auto handle : localStorage->m_Handles) + { + Point3D handleCenter = m_Impl->HandlePropertyList[i].GetPosition(); + + handle->SetRadius(handleSize); + handle->SetCenter(handleCenter[0], handleCenter[1], handleCenter[2] ); + + vtkMath::Normalize(normal); + double angle = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(normal, result0))); + double angle1 = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(normal, result1))); + double angle2 = vtkMath::DegreesFromRadians(acos(vtkMath::Dot(normal, result2))); + + // show handles only if the corresponding face is aligned to the render window + if (((abs(angle - 0)<0.001) || (abs(angle - 180)<0.001) && i != 0 && i != 1) + || ((abs(angle1 - 0)<0.001) || (abs(angle1 - 180)<0.001) && i != 2 && i != 3) + || ((abs(angle2 - 0)<0.001) || (abs(angle2 - 180)<0.001) && i != 4 && i != 5)) + { + + if (activeHandleId == nullptr) + { + appendPoly->AddInputConnection(handle->GetOutputPort()); + } + else + { + if ((activeHandleId->GetValue() != m_Impl->HandlePropertyList[i].GetIndex())) + { + appendPoly->AddInputConnection(handle->GetOutputPort()); + } + else + { + handle->Update(); + localStorage->m_SelectedHandleMapper->SetInputData(handle->GetOutput()); + localStorage->m_SelectedHandleActor->VisibilityOn(); + selected = true; + } + } + visible = true; + } + + i++; + } + + if (visible) + { + appendPoly->Update(); + } + else + { + localStorage->m_HandleActor->VisibilityOff(); + localStorage->m_SelectedHandleActor->VisibilityOff(); + } + + auto stripper = vtkSmartPointer::New(); + stripper->SetInputData(localStorage->m_Cutter->GetOutput()); + stripper->Update(); + + auto cutPolyData = vtkSmartPointer::New(); + cutPolyData->SetPoints(stripper->GetOutput()->GetPoints()); + cutPolyData->SetPolys(stripper->GetOutput()->GetLines()); + + localStorage->m_Actor->GetMapper()->SetInputDataObject(cutPolyData); + mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty("color")); + if (selectedColor != nullptr) + { + mitk::Color color = selectedColor->GetColor(); + localStorage->m_Actor->GetProperty()->SetColor(color[0], color[1], color[2]); + } + + if (activeHandleId != nullptr) + { + localStorage->m_HandleActor->GetProperty()->SetColor(1, 0, 0); + } + else + { + localStorage->m_HandleActor->GetProperty()->SetColor(1, 1, 1); + } + localStorage->m_HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput()); + + // add parts to the overall storage + localStorage->m_PropAssembly->AddPart(localStorage->m_Actor); + localStorage->m_PropAssembly->AddPart(localStorage->m_HandleActor); + if (selected) + { + localStorage->m_PropAssembly->AddPart(localStorage->m_SelectedHandleActor); + } + + localStorage->m_PropAssembly->VisibilityOn(); + localStorage->m_Actor->VisibilityOn(); + localStorage->m_HandleActor->VisibilityOn(); + } + else + { + localStorage->m_PropAssembly->VisibilityOff(); + localStorage->m_Actor->VisibilityOff(); + localStorage->m_HandleActor->VisibilityOff(); + localStorage->m_SelectedHandleActor->VisibilityOff(); + localStorage->UpdateGenerateDataTime(); + + } + localStorage->UpdateGenerateDataTime(); + } +} + +vtkProp* mitk::BoundingShapeVtkMapper2D::GetVtkProp(BaseRenderer* renderer) +{ + return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->m_PropAssembly; +} + +void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer*, vtkActor*) +{ + +} + diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp new file mode 100644 index 0000000000..ba7fa57be2 --- /dev/null +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp @@ -0,0 +1,340 @@ +/*=================================================================== + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center, +Division of Medical and Biological Informatics. +All rights reserved. + +This software is distributed WITHOUT ANY WARRANTY; without +even the implied warranty of MERCHANTABILITY or FITNESS FOR +A PARTICULAR PURPOSE. + +See LICENSE.txt or http://www.mitk.org for details. + +===================================================================*/ + +#include "../DataManagement/mitkBoundingShapeUtil.h" +#include +#include "mitkBoundingShapeVtkMapper3D.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +namespace mitk +{ + class BoundingShapeVtkMapper3D::Impl + { + public: + Impl() + : DistanceFromCam(1.0) + { + Point3D initialPoint; + initialPoint.Fill(0); + + for (int i = 0; i < 6; ++i) + HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i))); + } + + double DistanceFromCam; + std::vector HandlePropertyList; + LocalStorageHandler LocalStorageHandler; + }; +} + +mitk::BoundingShapeVtkMapper3D::LocalStorage::LocalStorage() + : m_Actor(vtkSmartPointer::New()), m_HandleActor(vtkSmartPointer::New()), m_SelectedHandleActor(vtkSmartPointer::New()), m_PropAssembly(vtkSmartPointer::New()) +{ + // initialize handles + for (int i = 0; i < 6; i++) + { + m_Handles.push_back(vtkSmartPointer::New()); + } + +} + +mitk::BoundingShapeVtkMapper3D::LocalStorage::~LocalStorage() +{ +} + +void mitk::BoundingShapeVtkMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool overwrite) +{ + if (node == nullptr) + return; + + Superclass::SetDefaultProperties(node, renderer, overwrite); +} + +mitk::BoundingShapeVtkMapper3D::BoundingShapeVtkMapper3D() + : m_Impl(new Impl) +{ +} + +mitk::BoundingShapeVtkMapper3D::~BoundingShapeVtkMapper3D() +{ + delete m_Impl; +} + +void mitk::BoundingShapeVtkMapper3D::ApplyColorAndOpacityProperties(BaseRenderer*, vtkActor*) +{ +} + +void mitk::BoundingShapeVtkMapper3D::ApplyBoundingShapeProperties(BaseRenderer* renderer, vtkActor* actor) +{ + if (actor == nullptr) + return; + + mitk::DataNode* dataNode = this->GetDataNode(); + + if (dataNode == nullptr) + return; + + bool render = false; + dataNode->GetBoolProperty("boundingshape.3drendering", render); + + actor->SetVisibility(render); + + float lineWidth = 1.0f; + dataNode->GetFloatProperty("boundingshape.line.width", lineWidth, renderer); + + vtkProperty* property = actor->GetProperty(); + property->SetLineWidth(lineWidth); + +} + +void mitk::BoundingShapeVtkMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) +{ + DataNode* node = this->GetDataNode(); + + if (node == nullptr) + return; + + + LocalStorage* localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer); + + vtkCamera* cam = 0; + const mitk::VtkPropRenderer *propRenderer = dynamic_cast(renderer); + if (propRenderer) + { + // get vtk renderer + vtkRenderer* vtkrenderer = propRenderer->GetVtkRenderer(); + if (vtkrenderer) + { + // get vtk camera + vtkCamera* vtkcam = vtkrenderer->GetActiveCamera(); + if (vtkcam) + { + // vtk smart pointer handling + cam = vtkcam; + cam->Register(nullptr); + } + } + } + + bool needGenerateData = localStorage->GetLastGenerateDataTime() < node->GetMTime(); + double distance = cam->GetDistance(); + if (abs(cam->GetDistance() - m_Impl->DistanceFromCam) > 0.001) + { + m_Impl->DistanceFromCam = distance; + needGenerateData = true; + } + + if (needGenerateData) + { + bool visible = true; + GetDataNode()->GetVisibility(visible, renderer, "visible"); + + if (!visible) + { + localStorage->m_Actor->VisibilityOff(); + return; + } + + // set the input-object at time t for the mapper + GeometryData* geometryData = dynamic_cast(node->GetData()); + if (geometryData == nullptr) + return; + + mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(); + mitk::Vector3D spacing = geometry->GetSpacing(); + + // calculate cornerpoints from geometry + std::vector cornerPoints = GetCornerPoints(geometry, true); + + Point3D p0 = cornerPoints[0]; + Point3D p1 = cornerPoints[1]; + Point3D p2 = cornerPoints[2]; + Point3D p4 = cornerPoints[4]; + + Point3D extent; + extent[0] = sqrt((p0[0] - p4[0])*(p0[0] - p4[0]) + (p0[1] - p4[1])*(p0[1] - p4[1]) + (p0[2] - p4[2])*(p0[2] - p4[2])); + extent[1] = sqrt((p0[0] - p2[0])*(p0[0] - p2[0]) + (p0[1] - p2[1])*(p0[1] - p2[1]) + (p0[2] - p2[2])*(p0[2] - p2[2])); + extent[2] = sqrt((p0[0] - p1[0])*(p0[0] - p1[0]) + (p0[1] - p1[1])*(p0[1] - p1[1]) + (p0[2] - p1[2])*(p0[2] - p1[2])); + + // calculate center based on half way of the distance between two opposing cornerpoints + mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); + + if (m_Impl->HandlePropertyList.size() == 6){ + // set handle positions + Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); + Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); + Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); + Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); + Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); + Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); + + m_Impl->HandlePropertyList[0].SetPosition(pointLeft); + m_Impl->HandlePropertyList[1].SetPosition(pointRight); + m_Impl->HandlePropertyList[2].SetPosition(pointTop); + m_Impl->HandlePropertyList[3].SetPosition(pointBottom); + m_Impl->HandlePropertyList[4].SetPosition(pointFront); + m_Impl->HandlePropertyList[5].SetPosition(pointBack); + } + + auto cube = vtkCubeSource::New(); + cube->SetXLength(extent[0] / spacing[0]); + cube->SetYLength(extent[1] / spacing[1]); + cube->SetZLength(extent[2] / spacing[2]); + + // calculates translation based on offset+extent not on the transformation matrix + vtkSmartPointer imageTransform = geometry->GetVtkTransform()->GetMatrix(); + auto translation = vtkSmartPointer::New(); + translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); + + auto transform = vtkSmartPointer::New(); + transform->SetMatrix(imageTransform); + transform->PostMultiply(); + transform->Concatenate(translation); + transform->Update(); + cube->Update(); + + auto transformFilter = vtkSmartPointer::New(); + transformFilter->SetInputData(cube->GetOutput()); + transformFilter->SetTransform(transform); + transformFilter->Update(); + cube->Delete(); + + + vtkSmartPointer polydata = transformFilter->GetPolyDataOutput(); + if (polydata == nullptr) + { + localStorage->m_Actor->VisibilityOff(); + return; + } + + mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast(this->GetDataNode()->GetProperty("handle size factor")); + + ScalarType initialHandleSize; + if (handleSizeProperty != nullptr) + initialHandleSize = handleSizeProperty->GetValue(); + else + initialHandleSize = 1.0/40.0; + + mitk::Point2D displaysize = renderer->GetDisplaySizeInMM(); + double handlesize = ((cam->GetDistance()*std::tan(vtkMath::RadiansFromDegrees(cam->GetViewAngle()))) / 2.0)*initialHandleSize; + + if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_HandleActor)) + localStorage->m_PropAssembly->RemovePart(localStorage->m_HandleActor); + if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_Actor)) + localStorage->m_PropAssembly->RemovePart(localStorage->m_Actor); + + auto selectedhandlemapper = vtkSmartPointer::New(); + auto appendPoly = vtkSmartPointer::New(); + + mitk::IntProperty::Pointer activeHandleId = dynamic_cast(node->GetProperty("BoundingShapeInteractor.active handle id")); + + int i = 0; + for (auto &handle : localStorage->m_Handles) + { + Point3D handlecenter = m_Impl->HandlePropertyList[i].GetPosition(); + handle->SetCenter(handlecenter[0], handlecenter[1], handlecenter[2]); + handle->SetRadius(handlesize); + handle->Update(); + if (activeHandleId == nullptr) + { + appendPoly->AddInputConnection(handle->GetOutputPort()); + } + else + { + if (activeHandleId->GetValue() != m_Impl->HandlePropertyList[i].GetIndex()) + { + appendPoly->AddInputConnection(handle->GetOutputPort()); + } + else + { + selectedhandlemapper->SetInputData(handle->GetOutput()); + localStorage->m_SelectedHandleActor->SetMapper(selectedhandlemapper); + localStorage->m_SelectedHandleActor->GetProperty()->SetColor(0, 1, 0); + localStorage->m_SelectedHandleActor->GetMapper()->SetInputDataObject(handle->GetOutput()); + localStorage->m_PropAssembly->AddPart(localStorage->m_SelectedHandleActor); + } + } + i++; + } + appendPoly->Update(); + + auto mapper = vtkSmartPointer::New(); + mapper->SetInputData(polydata); + + auto handlemapper = vtkSmartPointer::New(); + handlemapper->SetInputData(appendPoly->GetOutput()); + + localStorage->m_Actor->SetMapper(mapper); + localStorage->m_Actor->GetMapper()->SetInputDataObject(polydata); + localStorage->m_Actor->GetProperty()->SetOpacity(0.3); + + mitk::ColorProperty::Pointer selectedColor = dynamic_cast(node->GetProperty("color")); + if (selectedColor != nullptr){ + mitk::Color color = selectedColor->GetColor(); + localStorage->m_Actor->GetProperty()->SetColor(color[0], color[1], color[2]); + } + + localStorage->m_HandleActor->SetMapper(handlemapper); + if (activeHandleId == nullptr) + { + localStorage->m_HandleActor->GetProperty()->SetColor(1, 1, 1); + } + else + { + localStorage->m_HandleActor->GetProperty()->SetColor(1, 0, 0); + } + + localStorage->m_HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput()); + + this->ApplyColorAndOpacityProperties(renderer, localStorage->m_Actor); + this->ApplyBoundingShapeProperties(renderer, localStorage->m_Actor); + + this->ApplyColorAndOpacityProperties(renderer, localStorage->m_HandleActor); + this->ApplyBoundingShapeProperties(renderer, localStorage->m_HandleActor); + + this->ApplyColorAndOpacityProperties(renderer, localStorage->m_SelectedHandleActor); + this->ApplyBoundingShapeProperties(renderer, localStorage->m_SelectedHandleActor); + + // apply properties read from the PropertyList + this->ApplyProperties(localStorage->m_Actor, renderer); + this->ApplyProperties(localStorage->m_HandleActor, renderer); + this->ApplyProperties(localStorage->m_SelectedHandleActor, renderer); + + localStorage->m_Actor->VisibilityOn(); + localStorage->m_HandleActor->VisibilityOn(); + localStorage->m_SelectedHandleActor->VisibilityOn(); + + localStorage->m_PropAssembly->AddPart(localStorage->m_Actor); + localStorage->m_PropAssembly->AddPart(localStorage->m_HandleActor); + localStorage->m_PropAssembly->VisibilityOn(); + + localStorage->UpdateGenerateDataTime(); + } +} +vtkProp* mitk::BoundingShapeVtkMapper3D::GetVtkProp(BaseRenderer* renderer) +{ + return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->m_PropAssembly; +} + diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 62a0c5d924..282fbb5391 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,80 +1,81 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(mitk_modules Core CommandLine AppUtil DCMTesting RDF LegacyIO DataTypesExt Overlays LegacyGL AlgorithmsExt MapperExt DICOMReader DICOMReaderServices DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction ImageStatistics LegacyAdaptors SceneSerialization Gizmo GraphAlgorithms Multilabel ContourModel SurfaceInterpolation Segmentation PlanarFigureSegmentation OpenViewCore QtWidgets QtWidgetsExt QtWidgetsWeb QmlItems SegmentationUI DiffusionImaging GPGPU OpenIGTLink IGTBase IGT CameraCalibration RigidRegistration RigidRegistrationUI DeformableRegistration DeformableRegistrationUI OpenCL OpenCVVideoSupport QtOverlays ToFHardware ToFProcessing ToFUI US USUI DicomUI Simulation Remeshing Python QtPython Persistence OpenIGTLinkUI IGTUI VtkShaders DicomRT RTUI IOExt XNAT TubeGraph BiophotonicsHardware Classification TumorInvasionAnalysis MatchPointRegistration MatchPointRegistrationUI + BoundingShape ) if(MITK_ENABLE_PIC_READER) list(APPEND mitk_modules IpPicSupportIO) endif() diff --git a/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt b/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt index b3178a7154..4d2a1eebde 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt @@ -1,8 +1,8 @@ project(org_mitk_gui_qt_imagecropper) mitk_create_plugin( EXPORT_DIRECTIVE MITK_QT_IMAGECROPPER EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDS MitkQtWidgetsExt + MODULE_DEPENDS MitkQtWidgetsExt MitkBoundingShape MitkMultilabel ) diff --git a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/Advanced_ImageCropperView.png b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/Advanced_ImageCropperView.png new file mode 100644 index 0000000000..cd9fa4e650 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/Advanced_ImageCropperView.png differ diff --git a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/Basic_ImageCropperView.png b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/Basic_ImageCropperView.png new file mode 100644 index 0000000000..85b78cbb87 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/Basic_ImageCropperView.png differ diff --git a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/BoundingBox_ImageCropperView.png b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/BoundingBox_ImageCropperView.png new file mode 100644 index 0000000000..b2b870f138 Binary files /dev/null and b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/BoundingBox_ImageCropperView.png differ diff --git a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox index 11373662ad..584790fbde 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox +++ b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox @@ -1,36 +1,40 @@ /** -\page org_mitk_views_imagecropper The Image Cropper Plugin +\page org_mitk_gui_qt_imagecropper Image Cropper Plugin -\imageMacro{QmitkImageCropper_Icon.png,"Icon of the Image Cropper Plugin",2.00} +\imageMacro{QmitkImageCropper_Icon.png,"Icon of the Image Cropper Plugin.",20} \tableofcontents -\section QmitkImageCropperUserManualOverview Overview +\section org_mitk_gui_qt_imagecropperUsage Usage -ImageCropper is a functionality which allows the user to manually crop an image by means of a bounding box. The functionality does not create a new image, it only hides parts of the original image. +The Image Cropper Plugin allows you to crop subvolumes out of your orginial image volume by defining a cubic bounding box. -\section QmitkImageCropperUserManualFeatures Features +This box can be placed at an arbitrary position in the volume and can be easily adjusted by using the handles on each of the faces. +Touching the handles changes the size of the box whereas touching the box itself changes its position. -- Crop a selected image using a bounding box. -- Set the border voxels to a specific user defined value after cropping. +As soon as the bounding box is placed at the desired position, pressing the button 'Crop' creates a new image assigned to the orginal image +as child node containing only the selected subvolume. The size of the subvolume equals the size of the bounding box. +Pressing the "Mask" button keeps the original image size but masks out the area not contained within the bounding box bounds. +In case of 3D+t images the whole timeseries is cropped by default. +\imageMacro{BoundingBox_ImageCropperView.png,"Bounding Box.",12.00} +\imageMacro{Basic_ImageCropperView.png,"Basic Settings.",7.09} +\section org_mitk_gui_qt_imagecropperAdvanced Advanced settings +In the advanced settings view you find additional features to manipulate the bounding box. +\imageMacro{Advanced_ImageCropperView.png,"Advanced Settings.",7.09} -\section QmitkImageCropperUserManualUsage Usage -First select from the drop down menu the image to crop. The three 2D widgets show yellow rectangles representing the bounding box in each plane (axial, sagital, coronal), the lower right 3D widget shows the entire volume of the bounding box.\n +\subsection org_mitk_gui_qt_imagecropperAdvancedColor Bounding Shape Color +By pressing the two color buttons you may change the color for the selected and unselected state of the box. +\subsection org_mitk_gui_qt_imagecropperAdvancedOverwrite Overwrite original image +By enabling this checkbox the image is replaced by the cropped subvolume. Be careful to use this option since there is now Redo action available. +\subsection org_mitk_gui_qt_imagecropperAdvancedTimestep Crop current timestep only +If this checkbox is enabled the xD + t image is reduced to a xD image (3D+t --> 3D) with the timestep visible in the widget. This is useful if you want to extract a single image or its corresponding +subvolume of the timeseries. The whole timeseries is cropped by default using the timeGeometry of the timestep visible in the widget. -- To change the size of bounding box press control + right click and move the cursor up/down or left/right in one of the three 2D views.\n -- To change the orientation of the bounding box press control + middle click and move the cursor up/down or left/right in one of the three 2D views.\n -- To move the bounding box press control + left click and move the cursor to the wanted position in one of the three 2D views.\n +\section org_mitk_gui_qt_imagecropperIssues Current issues +Due to the latest changes in MITK slicegeometry is it currently not supported to crop 2D images unless the are 3D +images containing only a single slice. The user will be notified by a warning and and the input is handled as a single lable image. - -To show the result press the [crop] button.\n -To crop the image again press the [New bounding box!] button.\n\n - -All actions can be undone by using the global undo function (Ctrl+Z).\n - -To set the border voxels to a specific value after cropping the image, activate the corresponding checkbox and choose a gray value. - -\section QmitkImageCropperUserManualTroubleshooting Troubleshooting - -*/ +Right now changing the shape or rotation of the bounding box is not supported but might be integrated in the future. +*/ \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper_Icon.png b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper_Icon.png index a8695415b7..10649b8e8f 100644 Binary files a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper_Icon.png and b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper_Icon.png differ diff --git a/Plugins/org.mitk.gui.qt.imagecropper/files.cmake b/Plugins/org.mitk.gui.qt.imagecropper/files.cmake index 7c65fcb8a2..7dd76be335 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/files.cmake +++ b/Plugins/org.mitk.gui.qt.imagecropper/files.cmake @@ -1,37 +1,36 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES - mitkImageCropperPluginActivator.cpp + org_mitk_gui_qt_imagecropper_Activator.cpp QmitkImageCropper.cpp - mitkImageCropperEventInterface.cpp ) set(UI_FILES - src/internal/QmitkImageCropperControls.ui + src/internal/ImageCropperControls.ui ) set(MOC_H_FILES - src/internal/mitkImageCropperPluginActivator.h + src/internal/org_mitk_gui_qt_imagecropper_Activator.h src/internal/QmitkImageCropper.h ) set(CACHED_RESOURCE_FILES - resources/icon.xpm + resources/icon.png plugin.xml ) set(QRC_FILES resources/imagecropper.qrc ) set(CPP_FILES) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml b/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml index b995dde365..5239e23441 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml +++ b/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml @@ -1,30 +1,30 @@ Crop images to a given size diff --git a/Plugins/org.mitk.gui.qt.imagecropper/resources/icon.png b/Plugins/org.mitk.gui.qt.imagecropper/resources/icon.png index 9f896c0cda..fc9b34cb72 100644 Binary files a/Plugins/org.mitk.gui.qt.imagecropper/resources/icon.png and b/Plugins/org.mitk.gui.qt.imagecropper/resources/icon.png differ diff --git a/Plugins/org.mitk.gui.qt.imagecropper/resources/icon29.png b/Plugins/org.mitk.gui.qt.imagecropper/resources/icon29.png new file mode 100644 index 0000000000..604e5a76fb Binary files /dev/null and b/Plugins/org.mitk.gui.qt.imagecropper/resources/icon29.png differ diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/ImageCropperControls.ui b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/ImageCropperControls.ui new file mode 100644 index 0000000000..68363c2179 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/ImageCropperControls.ui @@ -0,0 +1,1304 @@ + + + ImageCropperControls + + + + 0 + 0 + 365 + 863 + + + + + 0 + 0 + + + + QmitkTemplate + + + + + + 3 + + + + + + 0 + 0 + + + + QLabel { color: rgb(255, 0, 0) } + + + Please select an image! + + + + + + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 63 + 63 + + + + + + + 127 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 63 + 63 + + + + + + + 127 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 120 + 120 + 120 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 63 + 63 + + + + + + + 127 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 127 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + Please select a bounding object! + + + + + + + + + false + + + + 0 + 0 + + + + + 0 + 90 + + + + Bounding object + + + + + 10 + 20 + 311 + 58 + + + + + + + + + + 0 + 0 + + + + + 16777215 + 16777215 + + + + + + + + + 0 + 0 + + + + New + + + + + + + + + + + + 0 + 0 + + + + Crop + + + + + + + + 0 + 0 + + + + Do Cropping + + + Mask + + + + + + + + + + + + + true + + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 63 + 63 + + + + + + + 127 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 63 + 63 + + + + + + + 127 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 255 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + + 127 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 127 + 127 + + + + + + + 255 + 63 + 63 + + + + + + + 127 + 0 + 0 + + + + + + + 170 + 0 + 0 + + + + + + + 127 + 0 + 0 + + + + + + + 255 + 255 + 255 + + + + + + + 127 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 0 + 0 + 0 + + + + + + + 255 + 0 + 0 + + + + + + + 255 + 255 + 220 + + + + + + + 0 + 0 + 0 + + + + + + + + ImageGeometry is rotated, result is not pixel-aligned. Please Reinit! + + + + + + + + 0 + 0 + + + + + 0 + 25 + + + + Advanced settings + + + false + + + 100 + + + Qt::ToolButtonTextBesideIcon + + + false + + + + + + + false + + + + 0 + 100 + + + + Bounding Shape + + + + + 10 + 20 + 321 + 71 + + + + + + + false + + + + Cuboid + + + + + Cone + + + + + Cylinder + + + + + Ellipsoid + + + + + + + + Reset bounding object + + + + + + + + + + + + 0 + 0 + + + + + 0 + 60 + + + + + 16777215 + 6777215 + + + + Bounding Shape Color + + + + + 20 + 20 + 309 + 31 + + + + + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + + + + + + + + Deselected + + + + + + + + 0 + 0 + + + + + + + + + + + Selected + + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 120 + + + + Output image settings + + + + + 10 + 30 + 321 + 161 + + + + + 10 + + + + + 6 + + + + + Outside Pixel Value: + + + + + + + false + + + + 0 + 0 + + + + + 16777210 + 16777215 + + + + + + + + + + Overwrite original image + + + false + + + + + + + Crop current timestep only (for timeseries) + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + QmitkDataStorageComboBox + QComboBox +
QmitkDataStorageComboBox.h
+
+ + ctkExpandButton + QToolButton +
ctkExpandButton.h
+
+
+ + +
diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.cpp index 7e32e45eb8..b4b159f5c2 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.cpp @@ -1,464 +1,632 @@ -/*=================================================================== +/*========================================================================= -The Medical Imaging Interaction Toolkit (MITK) +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. +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. -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ -#include +=========================================================================*/ #include "QmitkImageCropper.h" -#include +#include +#include +#include #include #include -#include #include #include -#include - -#include -#include -#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include // Includes for image casting between ITK and MITK: added after using Plugin Generator +#include +#include +#include +#include +#include +#include +#include +#include +#include #include #include -#include "mitkUndoController.h" -#include "mitkBoundingObjectCutter.h" -#include "mitkImageAccessByItk.h" -#include "mitkITKImageImport.h" -#include "mitkIDataStorageService.h" -#include "mitkNodePredicateDataType.h" +#include #include -//micro services #include #include -//to be moved to mitkInteractionConst.h by StateMachineEditor -const mitk::OperationType QmitkImageCropper::OP_EXCHANGE = 717; +const std::string QmitkImageCropper::VIEW_ID = "org.mitk.views.qmitkimagecropper"; -// constructors for operation classes -QmitkImageCropper::opExchangeNodes::opExchangeNodes( mitk::OperationType type, mitk::DataNode* node, mitk::BaseData* oldData, mitk::BaseData* newData ) -:mitk::Operation(type),m_Node(node),m_OldData(oldData),m_NewData(newData), m_NodeDeletedObserverTag(0), m_OldDataDeletedObserverTag(0), -m_NewDataDeletedObserverTag(0) +QmitkImageCropper::QmitkImageCropper(QObject *parent) + : m_ParentWidget(0), + m_ImageNode(nullptr), + m_CroppingObject(nullptr), + m_CroppingObjectNode(nullptr), + m_BoundingShapeInteractor(nullptr), + m_CropOutsideValue(0), + m_Advanced(0), + m_Active(0), + m_ScrollEnabled(true) { - // listen to the node the image is hold - itk::MemberCommand::Pointer nodeDeletedCommand = itk::MemberCommand::New(); - nodeDeletedCommand->SetCallbackFunction(this, &opExchangeNodes::NodeDeleted); - - m_NodeDeletedObserverTag = m_Node->AddObserver(itk::DeleteEvent(), nodeDeletedCommand); - m_OldDataDeletedObserverTag = m_OldData->AddObserver(itk::DeleteEvent(), nodeDeletedCommand); - m_NewDataDeletedObserverTag = m_NewData->AddObserver(itk::DeleteEvent(), nodeDeletedCommand); + CreateBoundingShapeInteractor(false); } -// destructor for operation class -QmitkImageCropper::opExchangeNodes::~opExchangeNodes() +QmitkImageCropper::~QmitkImageCropper() { - if (m_Node != NULL) - { - m_Node->RemoveObserver(m_NodeDeletedObserverTag); - m_Node=NULL; - } - - if (m_OldData.IsNotNull()) - { - m_OldData->RemoveObserver(m_OldDataDeletedObserverTag); - m_OldData=NULL; - } + //delete pointer objects + m_CroppingObjectNode = nullptr; + m_CroppingObject = nullptr; - if (m_NewData.IsNotNull()) + //disable interactor + if (m_BoundingShapeInteractor != nullptr) { - m_NewData->RemoveObserver(m_NewDataDeletedObserverTag); - m_NewData=NULL; + m_BoundingShapeInteractor->SetDataNode(nullptr); + m_BoundingShapeInteractor->EnableInteraction(false); } } -void QmitkImageCropper::opExchangeNodes::NodeDeleted(const itk::Object * /*caller*/, const itk::EventObject &/*event*/) +void QmitkImageCropper::SetFocus() { - m_Node = NULL; - m_OldData = NULL; - m_NewData = NULL; + m_Controls.buttonCreateNewBoundingBox->setFocus(); + m_Controls.comboBoxBoundingObject->setFocus(); } -QmitkImageCropper::QmitkImageCropper(QObject *parent) -: m_Controls(NULL), m_ParentWidget(0) +void QmitkImageCropper::CreateQtPartControl(QWidget *parent) { - m_Interface = new mitk::ImageCropperEventInterface; - m_Interface->SetImageCropper( this ); -} + // create GUI widgets from the Qt Designer's .ui file + m_Controls.setupUi(parent); + + m_Controls.boundingShapeSelector->SetDataStorage(this->GetDataStorage()); + m_Controls.boundingShapeSelector->SetPredicate(mitk::NodePredicateAnd::New( + mitk::TNodePredicateDataType::New(), + mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); + m_CroppingObjectNode = m_Controls.boundingShapeSelector->GetSelectedNode(); + + connect(m_Controls.buttonCropping, SIGNAL(clicked()), this, SLOT(DoCropping())); + connect(m_Controls.buttonMasking, SIGNAL(clicked()), this, SLOT(DoMasking())); + connect(m_Controls.boundingShapeSelector, SIGNAL(OnSelectionChanged(const mitk::DataNode*)), + this, SLOT(OnDataSelectionChanged(const mitk::DataNode*))); + connect(m_Controls.buttonCreateNewBoundingBox, SIGNAL(clicked()), this, SLOT(DoCreateNewBoundingObject())); + connect(m_Controls.buttonAdvancedSettings, SIGNAL(clicked()), this, SLOT(OnAdvancedSettingsButtonToggled())); + connect(m_Controls.buttonDeselectedColor, SIGNAL(clicked()), this, SLOT(OnDeselectedColorChanged())); + connect(m_Controls.buttonSelectedColor, SIGNAL(clicked()), this, SLOT(OnSelectedColorChanged())); + connect(m_Controls.spinBox, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int))); + + m_Controls.spinBox->setValue(-1000); + m_Controls.spinBox->setEnabled(false); + m_Controls.buttonCreateNewBoundingBox->setEnabled(false); + m_Controls.buttonCropping->setEnabled(false); + m_Controls.boundingShapeSelector->setEnabled(false); + m_Controls.labelWarningRotation->setVisible(false); + m_Controls.buttonAdvancedSettings->setEnabled(false); + + m_Advanced = false; + this->OnAdvancedSettingsButtonToggled(); + m_ParentWidget = parent; +} -QmitkImageCropper::~QmitkImageCropper() +void QmitkImageCropper::OnDataSelectionChanged(const mitk::DataNode* node) { - //delete smart pointer objects - m_CroppingObjectNode = NULL; - m_CroppingObject = NULL; + m_Controls.boundingShapeSelector->setEnabled(true); + m_CroppingObjectNode = m_Controls.boundingShapeSelector->GetSelectedNode(); + + if (m_CroppingObjectNode.IsNotNull() && dynamic_cast(this->m_CroppingObjectNode->GetData())) + { + m_Controls.buttonAdvancedSettings->setEnabled(true); + m_Controls.labelWarningBB->setText(QString::fromStdString("")); + m_CroppingObject = dynamic_cast(m_CroppingObjectNode->GetData()); + m_Advanced = true; + + mitk::ColorProperty::Pointer selcolorProperty = dynamic_cast + (m_CroppingObjectNode->GetProperty("BoundingShapeInteractor.selected color")); + mitk::ColorProperty::Pointer deselcolorProperty = dynamic_cast + (m_CroppingObjectNode->GetProperty("BoundingShapeInteractor.deselected color")); + if (selcolorProperty && deselcolorProperty) + { + mitk::Color deselColor = deselcolorProperty->GetColor(); + mitk::Color selColor = selcolorProperty->GetColor(); - m_Interface->Delete(); + QColor selCurrentColor((int)(selColor.GetRed() * 255), (int)(selColor.GetGreen() * 255), (int)(selColor.GetBlue() * 255)); + m_Controls.buttonSelectedColor->setAutoFillBackground(true); -} + auto styleSheet = QString("background-color:rgb(%1,%2,%3)") + .arg(selCurrentColor.red()) + .arg(selCurrentColor.green()) + .arg(selCurrentColor.blue()); -void QmitkImageCropper::CreateQtPartControl(QWidget* parent) -{ - if (!m_Controls) + m_Controls.buttonSelectedColor->setStyleSheet(styleSheet); + + QColor deselCurrentColor((int)(deselColor.GetRed() * 255), (int)(deselColor.GetGreen() * 255), (int)(deselColor.GetBlue() * 255)); + m_Controls.buttonDeselectedColor->setAutoFillBackground(true); + auto styleSheet2 = QString("background-color:rgb(%1,%2,%3)") + .arg(deselCurrentColor.red()) + .arg(deselCurrentColor.green()) + .arg(deselCurrentColor.blue()); + + m_Controls.buttonDeselectedColor->setStyleSheet(styleSheet2); + } + mitk::RenderingManager::GetInstance()->InitializeViews(); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + else { - m_ParentWidget = parent; - - // build ui elements - m_Controls = new Ui::QmitkImageCropperControls; - m_Controls->setupUi(parent); - - // setup ui elements - m_Controls->groupInfo->hide(); - m_Controls->m_SurroundingSlider->hide(); - m_Controls->m_SurroundingSpin->hide(); - m_Controls->m_BoxButton->setEnabled(true); - m_Controls->warningLabel->setVisible(false); - // create ui element connections - this->CreateConnections(); + m_Controls.buttonAdvancedSettings->setEnabled(false); + m_CroppingObject = nullptr; + m_BoundingShapeInteractor->EnableInteraction(false); + m_BoundingShapeInteractor->SetDataNode(nullptr); + m_Advanced = false; + this->OnAdvancedSettingsButtonToggled(); + QColor defaultColor((int)(255), (int)(255), (int)(255)); + auto styleSheet = QString("background-color:rgb(%1,%2,%3)") + .arg(defaultColor.red()) + .arg(defaultColor.green()) + .arg(defaultColor.blue()); + + m_Controls.buttonSelectedColor->setAutoFillBackground(true); + m_Controls.buttonSelectedColor->setStyleSheet(styleSheet); + m_Controls.buttonDeselectedColor->setAutoFillBackground(true); + m_Controls.buttonDeselectedColor->setStyleSheet(styleSheet); } } -void QmitkImageCropper::CreateConnections() +void QmitkImageCropper::OnAdvancedSettingsButtonToggled() { - if ( m_Controls ) - { - connect( m_Controls->m_CropButton, SIGNAL(clicked()), this, SLOT(CropImage())); // click on the crop button - connect( m_Controls->m_BoxButton, SIGNAL(clicked()), this, SLOT(CreateNewBoundingObject()) ); - connect( m_Controls->m_EnableSurroundingCheckBox, SIGNAL(toggled(bool)), this, SLOT(SurroundingCheck(bool)) ); - connect( m_Controls->chkInformation, SIGNAL(toggled(bool)), this, SLOT(ChkInformationToggled(bool)) ); - } + m_Controls.groupBoxShape->setVisible(m_Advanced); + m_Controls.groupBoxColors->setVisible(m_Advanced); + m_Controls.groupImageSettings->setVisible(m_Advanced); + m_Advanced = !m_Advanced; } -void QmitkImageCropper::Activated() +void QmitkImageCropper::CreateBoundingShapeInteractor(bool rotationEnabled) { - QmitkFunctionality::Activated(); // just call the inherited function + if (m_BoundingShapeInteractor.IsNull()) + { + m_BoundingShapeInteractor = mitk::BoundingShapeInteractor::New(); + m_BoundingShapeInteractor->LoadStateMachine("BoundingShapeInteraction.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); + m_BoundingShapeInteractor->SetEventConfig("BoundingShapeMouseConfig.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); + } + m_BoundingShapeInteractor->SetRotationEnabled(rotationEnabled); } -void QmitkImageCropper::Deactivated() +mitk::Geometry3D::Pointer QmitkImageCropper::InitializeWithImageGeometry(mitk::BaseGeometry::Pointer geometry) { - RemoveBoundingObjectFromNode(); - - QmitkFunctionality::Deactivated(); // just call the inherited function - - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + // convert a basegeometry into a Geometry3D (otherwise IO is not working properly) + if (geometry == nullptr) + mitkThrow() << "Geometry is not valid."; + + auto boundingGeometry = mitk::Geometry3D::New(); + boundingGeometry->SetBounds(geometry->GetBounds()); + boundingGeometry->SetImageGeometry(geometry->GetImageGeometry()); + boundingGeometry->SetOrigin(geometry->GetOrigin()); + boundingGeometry->SetSpacing(geometry->GetSpacing()); + boundingGeometry->SetIndexToWorldTransform(geometry->GetIndexToWorldTransform()); + boundingGeometry->Modified(); + return boundingGeometry; } -/*! When called with an opExchangeNodes, it changes the content of a node from one data set to another -*/ -void QmitkImageCropper::ExecuteOperation (mitk::Operation *operation) +void QmitkImageCropper::DoCreateNewBoundingObject() { - if (!operation) return; - - switch (operation->GetOperationType()) + if (m_ImageNode.IsNotNull()) { - case OP_EXCHANGE: + m_Controls.buttonCropping->setEnabled(true); + m_Controls.buttonMasking->setEnabled(true); + m_Controls.boundingShapeSelector->setEnabled(true); + + bool ok = false; + QString name = QInputDialog::getText(QApplication::activeWindow() + , "Add cropping shape...", "Enter name for the new cropping shape", QLineEdit::Normal, "BoundingShape", &ok); + if (!ok || name.isEmpty()) + return; + + // to do: check whether stdmulti.widget is valid + // get current timestep to support 3d+t images //to do: check if stdmultiwidget is valid + int timeStep = mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))->GetTimeStep(); + mitk::BaseGeometry::Pointer imageGeometry = static_cast(m_ImageNode->GetData()->GetGeometry(timeStep)); + + m_CroppingObject = mitk::GeometryData::New(); + m_CroppingObject->SetGeometry(static_cast(this->InitializeWithImageGeometry(imageGeometry))); + m_CroppingObjectNode = mitk::DataNode::New(); + m_CroppingObjectNode->SetData(m_CroppingObject); + m_CroppingObjectNode->SetProperty("name", mitk::StringProperty::New(name.toStdString())); + m_CroppingObjectNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); + m_CroppingObjectNode->SetProperty("opacity", mitk::FloatProperty::New(0.6)); + m_CroppingObjectNode->SetProperty("layer", mitk::IntProperty::New(99)); + m_CroppingObjectNode->AddProperty("isBoundingShape", mitk::BoolProperty::New(true)); + m_CroppingObjectNode->AddProperty("handle size factor", mitk::DoubleProperty::New(1.0 / 40.0)); + m_CroppingObjectNode->SetBoolProperty("pickable", true); + + if (!this->GetDataStorage()->Exists(m_CroppingObjectNode)) { - //RemoveBoundingObjectFromNode(); - opExchangeNodes* op = static_cast(operation); - op->GetNode()->SetData(op->GetNewData()); - mitk::RenderingManager::GetInstance()->InitializeViews(); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - break; + GetDataStorage()->Add(m_CroppingObjectNode, m_ImageNode); + m_Controls.boundingShapeSelector->SetSelectedNode(m_CroppingObjectNode); + m_CroppingObjectNode->SetVisibility(true); + m_BoundingShapeInteractor->EnableInteraction(true); + m_BoundingShapeInteractor->SetDataNode(this->m_CroppingObjectNode); + this->OnDataSelectionChanged(m_CroppingObjectNode); } - default:; } + // Adjust coordinate system by doing a reinit on + auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); + tempDataStorage->InsertElement(0, m_CroppingObjectNode); + // initialize the views to the bounding geometry + mitk::TimeGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); + mitk::RenderingManager::GetInstance()->InitializeViews(bounds); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } -void QmitkImageCropper::CreateNewBoundingObject() +void QmitkImageCropper::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, + const QList& nodes) { - // attach the cuboid to the image and update the views - if (this->IsVisible()) + bool rotationEnabled = false; + if (nodes.empty()) { - if (m_ImageNode.IsNotNull()) - { - m_ImageToCrop = dynamic_cast(m_ImageNode->GetData()); + m_Controls.labelWarningImage->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); + m_Controls.labelWarningImage->setText(QString::fromStdString("Select an image.")); + m_Controls.labelWarningBB->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); + m_Controls.labelWarningBB->setText(QString::fromStdString("No bounding object available.")); + m_Controls.buttonCreateNewBoundingBox->setEnabled(false); + m_Controls.buttonCropping->setEnabled(false); + m_Controls.buttonMasking->setEnabled(false); + m_Controls.comboBoxBoundingObject->setEnabled(false); + m_Controls.labelWarningRotation->setVisible(false); + return; + } + m_ParentWidget->setEnabled(true); - if(m_ImageToCrop.IsNotNull()) + foreach(mitk::DataNode::Pointer node, nodes) + { + if (node.IsNotNull() && dynamic_cast(node->GetData())) + { + m_ImageNode = nodes[0]; + m_Controls.groupBoundingObject->setEnabled(true); + m_Controls.comboBoxBoundingObject->setEnabled(true); + m_Controls.labelWarningImage->setStyleSheet(" QLabel { color: rgb(0, 0, 0) }"); + m_Controls.labelWarningImage->setText(QString::fromStdString("File name: " + m_ImageNode->GetName())); + m_Controls.buttonCreateNewBoundingBox->setEnabled(true); + + mitk::Image::Pointer image = dynamic_cast(m_ImageNode->GetData()); + if (image != nullptr) { - if (this->GetDefaultDataStorage()->GetNamedDerivedNode("CroppingObject", m_ImageNode)) + vtkSmartPointer imageMat = image->GetGeometry()->GetVtkMatrix(); + // check whether the image geometry is rotated, if so, no pixel aligned cropping or masking can be performed + if ((imageMat->GetElement(1, 0) == 0.0) && (imageMat->GetElement(0, 1) == 0.0) && + (imageMat->GetElement(1, 2) == 0.0) && (imageMat->GetElement(2, 1) == 0.0) && + (imageMat->GetElement(2, 0) == 0.0) && (imageMat->GetElement(0, 2) == 0.0)) { - //Remove m_Cropping - this->RemoveBoundingObjectFromNode(); + rotationEnabled = false; + m_Controls.labelWarningRotation->setVisible(false); } - - bool fitCroppingObject = false; - if(m_CroppingObject.IsNull()) + else { - CreateBoundingObject(); - fitCroppingObject = true; + rotationEnabled = true; + m_Controls.labelWarningRotation->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); + m_Controls.labelWarningRotation->setVisible(true); } - if (m_CroppingObject.IsNull()) - return; + this->CreateBoundingShapeInteractor(rotationEnabled); + m_CroppingObjectNode = m_Controls.boundingShapeSelector->GetSelectedNode(); + if (m_CroppingObjectNode != nullptr) + { + this->OnDataSelectionChanged(m_CroppingObjectNode); + m_BoundingShapeInteractor->EnableInteraction(true); + m_BoundingShapeInteractor->SetDataNode(this->m_CroppingObjectNode); + m_Controls.boundingShapeSelector->setEnabled(true); + } - AddBoundingObjectToNode( m_ImageNode, fitCroppingObject ); + if (image->GetPixelType().GetPixelType() == itk::ImageIOBase::SCALAR) + { + // TODO: ImageStatistics Plugin? Min/Max Value? + int minPixelValue = static_cast(image->GetScalarValueMin()); + //static_castimage->GetStatistics()->GetScalarValueMinNoRecompute(); + int maxPixelValue = static_cast(image->GetScalarValueMax()); + //static_castimage->GetStatistics()->GetScalarValueMaxNoRecompute(); + m_Controls.spinBox->setEnabled(true); + m_Controls.spinBox->setMaximum(maxPixelValue); + m_Controls.spinBox->setMinimum(minPixelValue); + m_Controls.spinBox->setValue(minPixelValue); + } + else + m_Controls.spinBox->setEnabled(false); - m_ImageNode->SetVisibility(true); - mitk::RenderingManager::GetInstance()->InitializeViews(); - mitk::RenderingManager::GetInstance()->RequestUpdateAll(); - m_Controls->m_BoxButton->setText("Reset bounding box!"); - m_Controls->m_CropButton->setEnabled(true); + unsigned int dim = image->GetDimension(); + if (dim < 2 || dim > 4) + { + m_Controls.labelWarningImage->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); + m_Controls.labelWarningImage->setText(QString::fromStdString("Select an image.")); + m_ParentWidget->setEnabled(false); + } + if (m_CroppingObjectNode != nullptr) + { + m_Controls.buttonCropping->setEnabled(true); + m_Controls.buttonMasking->setEnabled(true); + m_Controls.boundingShapeSelector->setEnabled(true); + m_Controls.labelWarningBB->setVisible(false); + } + else + { + m_Controls.buttonCropping->setEnabled(false); + m_Controls.buttonMasking->setEnabled(false); + m_Controls.boundingShapeSelector->setEnabled(false); + m_Controls.labelWarningBB->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); + m_Controls.labelWarningBB->setText(QString::fromStdString("No bounding object available.")); + } + return; } + // iterate all selected objects, adjust warning visibility + m_Controls.labelWarningImage->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); + m_Controls.labelWarningImage->setText(QString::fromStdString("Select an image.")); + m_Controls.buttonCropping->setEnabled(false); + m_Controls.buttonMasking->setEnabled(false); + m_Controls.buttonCreateNewBoundingBox->setEnabled(false); + m_Controls.comboBoxBoundingObject->setEnabled(false); + m_Controls.buttonDeselectedColor->setAutoFillBackground(false); + m_Controls.buttonDeselectedColor->setAutoFillBackground(false); + m_Controls.boundingShapeSelector->setEnabled(false); + m_ParentWidget->setEnabled(true); + m_Controls.labelWarningRotation->setVisible(false); } - else - QMessageBox::information(NULL, "Image cropping functionality", "Load an image first!"); } } +void QmitkImageCropper::OnSelectedColorChanged() +{ + mitk::ColorProperty::Pointer colorProperty = dynamic_cast(m_CroppingObjectNode->GetProperty("BoundingShapeInteractor.selected color")); + this->ChangeColor(colorProperty, true); +} -void QmitkImageCropper::SurroundingCheck(bool value) +void QmitkImageCropper::OnDeselectedColorChanged() { - if(value) + mitk::ColorProperty* colorProperty = dynamic_cast(m_CroppingObjectNode->GetProperty("BoundingShapeInteractor.deselected color")); + this->ChangeColor(colorProperty, false); + +} + +void QmitkImageCropper::ChangeColor(mitk::ColorProperty::Pointer colorProperty, bool selected) +{ + if (m_CroppingObjectNode != nullptr) { - if(m_ImageNode.IsNotNull()) + if (colorProperty) { - mitk::DataNode *imageNode = m_ImageNode.GetPointer(); - if (imageNode) + mitk::Color color = colorProperty->GetColor(); + QColor currentColor((int)(color.GetRed() * 255), (int)(color.GetGreen() * 255), (int)(color.GetBlue() * 255)); + QColor result = QColorDialog::getColor(currentColor); + if (result.isValid()) { - mitk::BaseData* data = imageNode->GetData(); - if (data) - { - // test if this data item is an image or not (could also be a surface or something totally different) - mitk::Image* image = dynamic_cast( data ); - if (image) - { - float min = 10000.0; - float max = -10000.0; + color.SetRed(result.red() / 255.0); + color.SetGreen(result.green() / 255.0); + color.SetBlue(result.blue() / 255.0); + colorProperty->SetColor(color); - min = image->GetScalarValueMin(); - max = image->GetScalarValueMax(); + auto styleSheet = QString("background-color:rgb(%1,%2,%3)") + .arg(result.red()) + .arg(result.green()) + .arg(result.blue()); - m_Controls->m_SurroundingSlider->setRange((int)min,(int)max); - m_Controls->m_SurroundingSpin->setRange((int)min,(int)max); - } + if (!selected) + { + m_Controls.buttonDeselectedColor->setAutoFillBackground(true); + m_Controls.buttonDeselectedColor->setStyleSheet(styleSheet); } + else + { + m_Controls.buttonSelectedColor->setAutoFillBackground(true); + m_Controls.buttonSelectedColor->setStyleSheet(styleSheet); + } + m_BoundingShapeInteractor->SetDataNode(m_CroppingObjectNode); + mitk::RenderingManager::GetInstance()->InitializeViews(); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } - m_Controls->m_SurroundingSlider->show(); - m_Controls->m_SurroundingSpin->show(); } - else - m_Controls->m_EnableSurroundingCheckBox->setChecked(false); - } - else - { - m_Controls->m_SurroundingSlider->hide(); - m_Controls->m_SurroundingSpin->hide(); } } -void QmitkImageCropper::CropImage() +void QmitkImageCropper::OnComboBoxSelectionChanged(const mitk::DataNode* node) { - // test, if image is selected - if (m_ImageToCrop.IsNull()) return; - - // test, if bounding box is visible - if (m_CroppingObjectNode.IsNull()) - { - QMessageBox::information(NULL, "Image cropping functionality", "Generate a new bounding object first!"); - return; - } - - // image and bounding object ok - mitk::BoundingObjectCutter::Pointer cutter = mitk::BoundingObjectCutter::New(); - cutter->SetBoundingObject( m_CroppingObject ); - cutter->SetInput( m_ImageToCrop ); - cutter->AutoOutsideValueOff(); - - if (m_Controls->m_EnableSurroundingCheckBox->isChecked()) - { - cutter->SetOutsideValue(m_Controls->m_SurroundingSpin->value()); - } - - // do the actual cutting - try - { - cutter->Update(); - //cutter->UpdateLargestPossibleRegion(); - } - catch(const itk::ExceptionObject& e) + mitk::DataNode* selectedNode = const_cast(node); + if (selectedNode != nullptr) { - std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); - - QMessageBox::warning ( NULL, - tr("Cropping not possible!"), - tr(message.c_str()), - QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton ); - return; + if (m_ImageNode.IsNotNull()) + selectedNode->SetDataInteractor(m_ImageNode->GetDataInteractor()); + // m_ImageNode->GetDataInteractor()->SetDataNode(selectedNode); + m_ImageNode = selectedNode; } +} - // cutting successful - mitk::Image::Pointer resultImage = cutter->GetOutput(); - resultImage->DisconnectPipeline(); - resultImage->SetPropertyList(m_ImageToCrop->GetPropertyList()->Clone()); - - RemoveBoundingObjectFromNode(); +void QmitkImageCropper::OnSliderValueChanged(int slidervalue) +{ + m_CropOutsideValue = slidervalue; +} - { - opExchangeNodes* doOp = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(), - m_ImageNode->GetData(), - resultImage); - opExchangeNodes* undoOp = new opExchangeNodes(OP_EXCHANGE, m_ImageNode.GetPointer(), - resultImage, - m_ImageNode->GetData()); - - // TODO: MITK doesn't recognize that a new event happens in the next line, - // because nothing happens in the render window. - // As a result the undo action will happen together with the last action - // recognized by MITK. - mitk::OperationEvent* operationEvent = new mitk::OperationEvent( m_Interface, doOp, undoOp, "Crop image"); - mitk::UndoController::GetCurrentUndoModel()->SetOperationEvent( operationEvent ); // tell the undo controller about the action - - ExecuteOperation(doOp); // execute action - } - m_Controls->m_BoxButton->setEnabled(true); - m_Controls->m_CropButton->setEnabled(false); +void QmitkImageCropper::DoMasking() +{ + this->ProcessImage(true); } -void QmitkImageCropper::CreateBoundingObject() +void QmitkImageCropper::DoCropping() { - QStringList items; - items << tr("Cuboid") << tr("Ellipsoid") << tr("Cylinder") << tr("Cone"); - - bool ok; - QString item = QInputDialog::getItem(m_Parent, tr("Select Bounding Object"), tr("Type of Bounding Object:"), items, 0, false, &ok); + this->ProcessImage(false); +} - if (!ok) - return; - if (item == "Ellipsoid") - m_CroppingObject = mitk::Ellipsoid::New(); - else if(item == "Cylinder") - m_CroppingObject = mitk::Cylinder::New(); - else if (item == "Cone") - m_CroppingObject = mitk::Cone::New(); - else if (item == "Cuboid") - m_CroppingObject = mitk::Cuboid::New(); - else - return; +void QmitkImageCropper::ProcessImage(bool mask) +{ + // cropping only possible if valid bounding shape as well as a valid image are loaded + QList nodes = this->GetDataManagerSelection(); - m_CroppingObjectNode = mitk::DataNode::New(); - m_CroppingObjectNode->SetData( m_CroppingObject ); - m_CroppingObjectNode->SetProperty( "name", mitk::StringProperty::New( "CroppingObject" ) ); - m_CroppingObjectNode->SetProperty( "color", mitk::ColorProperty::New(1.0, 1.0, 0.0) ); - m_CroppingObjectNode->SetProperty( "opacity", mitk::FloatProperty::New(0.4) ); - m_CroppingObjectNode->SetProperty( "layer", mitk::IntProperty::New(99) ); // arbitrary, copied from segmentation functionality - m_CroppingObjectNode->SetProperty( "helper object", mitk::BoolProperty::New(true) ); + // to do: check whether stdmultiwidget is valid + int timeStep = mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget1"))->GetTimeStep(); - mitk::AffineImageCropperInteractor::Pointer affineDataInteractor = mitk::AffineImageCropperInteractor::New(); - affineDataInteractor->LoadStateMachine("ClippingPlaneInteraction3D.xml", us::ModuleRegistry::GetModule("MitkDataTypesExt")); - affineDataInteractor->SetEventConfig("CropperDeformationConfig.xml", us::ModuleRegistry::GetModule("MitkDataTypesExt")); - affineDataInteractor->SetDataNode(m_CroppingObjectNode); - m_CroppingObjectNode->SetBoolProperty("pickable", true); + if (nodes.empty()) return; -} - -void QmitkImageCropper::OnSelectionChanged(std::vector nodes) -{ - this->RemoveBoundingObjectFromNode(); + mitk::DataNode* node = nodes[0]; - if (nodes.size() != 1 || dynamic_cast(nodes[0]->GetData()) == 0) + if (node == nullptr) { - m_ParentWidget->setEnabled(false); - m_Controls->warningLabel->setVisible(false); + QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); return; } - - m_ImageNode = nodes[0]; - m_ParentWidget->setEnabled(true); - // do not accept datanodes with dimension of less than three - mitk::Image* m_ImageToCrop = dynamic_cast(nodes[0]->GetData()); - if (m_ImageToCrop == NULL) + if (m_CroppingObject == nullptr) { + QMessageBox::information(nullptr, "Warning", "Please load and select a cropping object before starting image processing."); return; } - unsigned int dim = m_ImageToCrop->GetDimension(); - if (dim < 3) - { - m_Controls->warningLabel->setVisible(true); - m_ParentWidget->setEnabled(false); - } - else - { - m_Controls->warningLabel->setVisible(false); - } -} -void QmitkImageCropper::AddBoundingObjectToNode(mitk::DataNode* node, bool fit) -{ - m_ImageToCrop = dynamic_cast(node->GetData()); - unsigned int dim = m_ImageToCrop->GetDimension(); - if (dim < 3) - { - MITK_WARN << "Image Cropper does not support 1D/2D Objects. Aborting operation"; - return; - } - if(!this->GetDefaultDataStorage()->Exists(m_CroppingObjectNode)) + mitk::BaseData* data = node->GetData(); //get data from node + if (data != nullptr) { - this->GetDefaultDataStorage()->Add(m_CroppingObjectNode, node); - if (fit) + QString imageName; + if (mask) + imageName = QString::fromStdString(node->GetName() + "_masked"); + else + imageName = QString::fromStdString(node->GetName() + "_cropped"); + + // image and bounding shape ok, set as input + auto croppedImageNode = mitk::DataNode::New(); + auto cutter = mitk::BoundingShapeCropper::New(); + cutter->SetGeometry(m_CroppingObject); + + // adjustable in advanced settings + cutter->SetUseWholeInputRegion(mask); //either mask (mask=true) or crop (mask=false) + cutter->SetOutsideValue(m_CropOutsideValue); + cutter->SetUseCropTimeStepOnly(m_Controls.checkBoxCropTimeStepOnly->isChecked()); + cutter->SetCurrentTimeStep(timeStep); + + // TODO: Add support for MultiLayer (right now only Mulitlabel support) + mitk::LabelSetImage* labelsetImageInput = dynamic_cast(data); + if (labelsetImageInput != nullptr) { - m_CroppingObject->FitGeometry(m_ImageToCrop->GetGeometry()); - } + cutter->SetInput(labelsetImageInput); + // do the actual cutting + try + { + cutter->Update(); + } + catch (const itk::ExceptionObject& e) + { + std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); + QMessageBox::warning(nullptr, + tr("Cropping not possible!"), + tr(message.c_str()), + QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); + return; + } - //mitk::GlobalInteraction::GetInstance()->AddInteractor( m_AffineInteractor ); - } - m_CroppingObjectNode->SetVisibility(true); -} + auto labelSetImage = mitk::LabelSetImage::New(); + labelSetImage->InitializeByLabeledImage(cutter->GetOutput()); -void QmitkImageCropper::RemoveBoundingObjectFromNode() -{ - if (m_CroppingObjectNode.IsNotNull()) - { - if(this->GetDefaultDataStorage()->Exists(m_CroppingObjectNode)) - { - this->GetDefaultDataStorage()->Remove(m_CroppingObjectNode); - //mitk::GlobalInteraction::GetInstance()->RemoveInteractor(m_AffineInteractor); - m_CroppingObject = NULL; - } - m_Controls->m_BoxButton->setText("New bounding box!"); - } -} - -void QmitkImageCropper::ChkInformationToggled( bool on ) -{ - if (on) - m_Controls->groupInfo->show(); - else - m_Controls->groupInfo->hide(); -} + for (int i = 0; i < labelsetImageInput->GetNumberOfLayers(); i++) + { + labelSetImage->AddLabelSetToLayer(i, labelsetImageInput->GetLabelSet(i)); + } -void QmitkImageCropper::StdMultiWidgetAvailable( QmitkStdMultiWidget& stdMultiWidget ) -{ - m_MultiWidget = &stdMultiWidget; -} + croppedImageNode->SetData(labelSetImage); + croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); -void QmitkImageCropper::StdMultiWidgetNotAvailable() -{ - m_MultiWidget = NULL; -} + //add cropping result to the current data storage as child node to the image node + if (!m_Controls.checkOverwriteImage->isChecked()) + { + if (!this->GetDataStorage()->Exists(croppedImageNode)) + { + this->GetDataStorage()->Add(croppedImageNode, m_ImageNode); + } + } + else // original image will be overwritten by the result image and the bounding box of the result is adjusted + { + node->SetData(labelSetImage); + node->Modified(); + // Adjust coordinate system by doing a reinit on + auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); + tempDataStorage->InsertElement(0, node); + + // initialize the views to the bounding geometry + mitk::TimeGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); + mitk::RenderingManager::GetInstance()->InitializeViews(bounds); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + } + else + { + mitk::Image::Pointer imageInput = dynamic_cast(data); + if (imageInput != nullptr) + { + cutter->SetInput(imageInput); + // do the actual cutting + try + { + cutter->Update(); + } + catch (const itk::ExceptionObject& e) + { + std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); -void QmitkImageCropper::NodeRemoved(const mitk::DataNode *node) -{ - std::string name = node->GetName(); + QMessageBox::warning(nullptr, + tr("Cropping not possible!"), + tr(message.c_str()), + QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); + return; + } - if (strcmp(name.c_str(), "CroppingObject")==0) + //add cropping result to the current data storage as child node to the image node + if (!m_Controls.checkOverwriteImage->isChecked()) + { + croppedImageNode->SetData(cutter->GetOutput()); + croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); + croppedImageNode->SetProperty("color", mitk::ColorProperty::New(1.0, 0.0, 0.0)); + croppedImageNode->SetProperty("opacity", mitk::FloatProperty::New(0.4)); + croppedImageNode->SetProperty("layer", mitk::IntProperty::New(99)); // arbitrary, copied from segmentation functionality + if (!this->GetDataStorage()->Exists(croppedImageNode)) + { + this->GetDataStorage()->Add(croppedImageNode, m_ImageNode); + } + } + else // original image will be overwritten by the result image and the bounding box of the result is adjusted + { + node->SetData(cutter->GetOutput()); + node->Modified(); + // Adjust coordinate system by doing a reinit on + auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); + tempDataStorage->InsertElement(0, node); + + // initialize the views to the bounding geometry + mitk::TimeGeometry::Pointer bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); + mitk::RenderingManager::GetInstance()->InitializeViews(bounds); + mitk::RenderingManager::GetInstance()->RequestUpdateAll(); + } + } + } + } + else { - m_Controls->m_CropButton->setEnabled(false); - m_Controls->m_BoxButton->setEnabled(true); + QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); } } + + diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.h b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.h index 1e1efb13ee..127c3fbd0a 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.h +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropper.h @@ -1,212 +1,179 @@ -/*=================================================================== +/*========================================================================= -The Medical Imaging Interaction Toolkit (MITK) +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html for details. -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. +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. -See LICENSE.txt or http://www.mitk.org for details. +=========================================================================*/ -===================================================================*/ +#ifndef QmitkImageCropper_h +#define QmitkImageCropper_h -#if !defined(QMITK_IMAGECROPPER_H__INCLUDED) -#define QMITK_IMAGECROPPER_H__INCLUDED +#include #ifdef WIN32 #pragma warning( disable : 4250 ) #endif -#include "QmitkFunctionality.h" -#include -#include -#include -#include -#include "mitkWeakPointer.h" -#include #include +#include "QVTKWidget.h" +#include "QmitkRegisterClasses.h" -#include "mitkImageCropperEventInterface.h" +#include "itkCommand.h" +#include +#include -#include "ui_QmitkImageCropperControls.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include -/*! -\ingroup org_mitk_gui_qt_imagecropper_internal -\brief Functionality for cropping images with a cuboid +#include "ui_ImageCropperControls.h" -This functionality lets the user select an image from the data tree, select an area of interest by placing -a cuboid object, and then crop the image, so that pixels from outside the cuboid will remove. +#include "usServiceRegistration.h" -The image size is automatically reduced, if the cuboid is not rotated but parallel to the image axes. -\b Implementation -The functionality owns a cuboid (m_CroppingObject) and the associated interactor (m_AffineInteractor), -which implements moving and scaling the cuboid. +/*! +@brief QmitkImageCropperView +\warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. +\sa QmitkFunctionality +\ingroup ${plugin_target}_internal */ - -class QmitkImageCropper : public QmitkFunctionality, public mitk::OperationActor +class QmitkImageCropper : public QmitkAbstractView { - - /// Operation base class, which holds pointers to a node of the data tree (mitk::DataNode) - /// and to two data sets (mitk::BaseData) instances - class opExchangeNodes: public mitk::Operation - { - public: opExchangeNodes( mitk::OperationType type, mitk::DataNode* node, - mitk::BaseData* oldData, - mitk::BaseData* newData ); - ~opExchangeNodes(); - mitk::DataNode* GetNode() { return m_Node; } - mitk::BaseData* GetOldData() { return m_OldData; } - mitk::BaseData* GetNewData() { return m_NewData; } - protected: - void NodeDeleted(const itk::Object * /*caller*/, const itk::EventObject & /*event*/); - private: - mitk::DataNode* m_Node; - mitk::BaseData::Pointer m_OldData; - mitk::BaseData::Pointer m_NewData; - long m_NodeDeletedObserverTag; - long m_OldDataDeletedObserverTag; - long m_NewDataDeletedObserverTag; - }; - + // this is needed for all Qt objects that should have a Qt meta-object + // (everything that derives from QObject and wants to have signal/slots) private: Q_OBJECT public: /*! - \brief Constructor. Called by SampleApp (or other apps that use functionalities) + @brief Constructor. Called by SampleApp (or other apps that use functionalities) */ - QmitkImageCropper(QObject *parent=0); + QmitkImageCropper(QObject *parent = 0); - /*! - \brief Destructor - */ virtual ~QmitkImageCropper(); + static const std::string VIEW_ID; + + virtual void CreateQtPartControl(QWidget *parent); + /*! - \brief Creates the Qt widget containing the functionality controls, like sliders, buttons etc. + @brief Creates the Qt connections needed */ - virtual void CreateQtPartControl(QWidget* parent) override; + QWidget* GetControls(); + + /// @brief Called when the user clicks the GUI button + protected slots: /*! - \brief Creates the Qt connections needed + * @brief Creates a new bounding object */ - virtual void CreateConnections(); - + virtual void DoCreateNewBoundingObject(); /*! - \brief Invoked when this functionality is selected by the application + * @brief Whenever Crop button is pressed, issue a cropping action */ - virtual void Activated() override; - + void DoCropping(); /*! - \brief Invoked when the user leaves this functionality + * @brief Whenever Mask button is pressed, issue a masking action */ - virtual void Deactivated() override; - - /// - /// Called when a StdMultiWidget is available. - /// - virtual void StdMultiWidgetAvailable(QmitkStdMultiWidget& stdMultiWidget) override; - /// - /// Called when no StdMultiWidget is available. - /// - virtual void StdMultiWidgetNotAvailable() override; - - - /* - \brief Interface of a mitk::StateMachine (for undo/redo) + void DoMasking(); + /*! + * @brief Dis- or enable the advanced setting section */ - virtual void ExecuteOperation (mitk::Operation*) override; - - QWidget* GetControls(); - - - public slots: - - virtual void CropImage(); - virtual void SurroundingCheck(bool value); - virtual void CreateNewBoundingObject(); - virtual void ChkInformationToggled( bool on ); - -protected: - + void OnAdvancedSettingsButtonToggled(); /*! - * Default main widget containing 4 windows showing 3 - * orthogonal slices of the volume and a 3d render window + * @brief Updates current selection of the bounding object */ - QmitkStdMultiWidget* m_MultiWidget; - + void OnDataSelectionChanged(const mitk::DataNode* node); /*! - * Controls containing an image selection drop down, some usage information and a "crop" button + * @brief Changes the colors of the bounding object */ - Ui::QmitkImageCropperControls * m_Controls; - + void OnSelectedColorChanged(); /*! - * The parent QWidget + * @brief Changes the colors of the bounding object */ - QWidget* m_ParentWidget; - + void OnDeselectedColorChanged(); /*! - * \brief A pointer to the node of the image to be croped. + * @brief Sets the scalar value for outside pixels in case of masking */ - mitk::WeakPointer m_ImageNode; + void OnSliderValueChanged(int slidervalue); +protected: + + virtual void SetFocus(); /*! - * \brief A pointer to the image to be cropped. + @brief called by QmitkFunctionality when DataManager's selection has changed */ - mitk::WeakPointer m_ImageToCrop; - + void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList& nodes) override; /*! - * \brief The cuboid used for cropping. + @brief Sets the selected bounding object as current bounding object and set up interactor */ - mitk::BoundingObject::Pointer m_CroppingObject; - + void OnComboBoxSelectionChanged(const mitk::DataNode* node); /*! - * \brief Tree node of the cuboid used for cropping. + @brief Change color of the selected or deselected bounding shape */ - mitk::DataNode::Pointer m_CroppingObjectNode; - + void ChangeColor(mitk::ColorProperty::Pointer colorProperty, bool selected); /*! - * \brief Interactor for moving and scaling the cuboid + * @brief Initializes a new bounding shape using the selected image geometry. */ - mitk::AffineImageCropperInteractor::Pointer m_AffineInteractor; + mitk::Geometry3D::Pointer InitializeWithImageGeometry(mitk::BaseGeometry::Pointer geometry); + void CreateBoundingShapeInteractor(bool rotationEnabled); + +private: /*! - * \brief Creates the cuboid and its data tree node. + * The parent QWidget */ - virtual void CreateBoundingObject(); - + QWidget* m_ParentWidget; /*! - * \brief Called from superclass, handles selection changes. + * @brief A pointer to the node of the image to be cropped. */ - virtual void OnSelectionChanged(std::vector nodes) override; - + mitk::WeakPointer m_ImageNode; /*! - * \brief Finds the given node in the data tree and optionally fits the cuboid to it + * @brief The cuboid used for cropping. */ - virtual void AddBoundingObjectToNode(mitk::DataNode* node, bool fit); + mitk::GeometryData::Pointer m_CroppingObject; /*! - * \brief Removes the cuboid from any node and hides it from the user. + * @brief Tree node of the cuboid used for cropping. */ - virtual void RemoveBoundingObjectFromNode(); + mitk::DataNode::Pointer m_CroppingObjectNode; - virtual void NodeRemoved(const mitk::DataNode* node) override; + /*! + * @brief Interactor for moving and scaling the cuboid + */ + mitk::BoundingShapeInteractor::Pointer m_BoundingShapeInteractor; -private: + void ProcessImage(bool crop); - // operation constant - static const mitk::OperationType OP_EXCHANGE; + // cropping parameter + mitk::ScalarType m_CropOutsideValue; + bool m_Advanced; + bool m_Active; + bool m_ScrollEnabled; - //Interface class for undo redo - mitk::ImageCropperEventInterface* m_Interface; + Ui::ImageCropperControls m_Controls; }; -#endif // !defined(QMITK_IMAGECROPPER_H__INCLUDED) + + +#endif // QmitkImageCropper_h \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperControls.ui b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperControls.ui deleted file mode 100644 index ade64b5622..0000000000 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperControls.ui +++ /dev/null @@ -1,444 +0,0 @@ - - - QmitkImageCropperControls - - - - 0 - 0 - 422 - 651 - - - - - 0 - 0 - - - - BoundingObjectImageCropper - - - - - - true - - - - - - - - 170 - 0 - 0 - - - - - - - - - 170 - 0 - 0 - - - - - - - - - 170 - 0 - 0 - - - - - - - - The image cropper cannot handle 2D images - - - - - - - false - - - - 0 - 0 - - - - crop the selected image - - - Crop - - - - - - - false - - - - 0 - 0 - - - - crop the selected image - - - New bounding box! - - - - - - - - - -2000 - - - 2000 - - - -1000 - - - - - - - - 0 - 0 - - - - Set border voxel value - - - false - - - - - - - -2000 - - - 2000 - - - -1000 - - - Qt::Horizontal - - - - - - - - - - 0 - 0 - - - - Show usage information - - - - - - - - 24 - - - 0 - - - 0 - - - 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'; font-size:10pt; 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;">The yellow box around the selected image can be changed in size and orientation. You can use it to mark an interesing region of the image and then use the &quot;Crop&quot; button to cut off parts of the image that are outside the box.<br />In order to undo/redo operations, please use the corresponding buttons in the workbench. </p></body></html> - - - Qt::AlignVCenter - - - true - - - - - - - - - - Qt::Vertical - - - QSizePolicy::Minimum - - - - 20 - 20 - - - - - - - - - - - 0 - 0 - - - - :/imagecropper/btn_ctrl.xpm - - - false - - - false - - - - - - - - 0 - 0 - - - - :/imagecropper/mouse_left.xpm - - - false - - - false - - - - - - - Move the box - - - false - - - - - - - - - - - - 0 - 0 - - - - - - - :/imagecropper/btn_ctrl.xpm - - - false - - - false - - - - - - - - 0 - 0 - - - - :/imagecropper/mouse_right.xpm - - - false - - - false - - - - - - - Resize the box - - - false - - - - - - - - - - - - 0 - 0 - - - - - - - :/imagecropper/btn_ctrl.xpm - - - false - - - false - - - - - - - - 0 - 0 - - - - :/imagecropper/mouse_middle.xpm - - - false - - - false - - - - - - - Rotate the box - - - false - - - - - - - - - Qt::Vertical - - - QSizePolicy::Expanding - - - - 20 - 200 - - - - - - - - - QmitkDataStorageComboBox.h - - - - - - - - - m_SurroundingSpin - valueChanged(int) - m_SurroundingSlider - setValue(int) - - - 20 - 20 - - - 20 - 20 - - - - - m_SurroundingSlider - valueChanged(int) - m_SurroundingSpin - setValue(int) - - - 20 - 20 - - - 20 - 20 - - - - - diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperEventInterface.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperEventInterface.cpp deleted file mode 100644 index eea7c22008..0000000000 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperEventInterface.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include "QmitkImageCropper.h" -#include "mitkImageCropperEventInterface.h" - - -mitk::ImageCropperEventInterface::ImageCropperEventInterface() -{ -} - -mitk::ImageCropperEventInterface::~ImageCropperEventInterface() -{ -} - -void mitk::ImageCropperEventInterface::ExecuteOperation(mitk::Operation* op) -{ - m_ImageCropper->ExecuteOperation( op ); -} diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperEventInterface.h b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperEventInterface.h deleted file mode 100644 index c18d968fb4..0000000000 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperEventInterface.h +++ /dev/null @@ -1,51 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#ifndef MITKIMAGECROPPEREVENTINTERFACE_H -#define MITKIMAGECROPPEREVENTINTERFACE_H - -#include "itkObject.h" - -#include "mitkOperation.h" -#include "mitkOperationActor.h" - -class QmitkImageCropper; - -namespace mitk -{ - class ImageCropperEventInterface : public itk::Object, public OperationActor - { - - public: - - ImageCropperEventInterface(); - ~ImageCropperEventInterface(); - - void SetImageCropper( QmitkImageCropper* imageCropper ) - { - m_ImageCropper = imageCropper; - } - - virtual void ExecuteOperation(mitk::Operation* op) override; - - private: - - QmitkImageCropper* m_ImageCropper; - - }; -} - -#endif // MITKIMAGECROPPEREVENTINTERFACE_H diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperPluginActivator.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperPluginActivator.cpp deleted file mode 100644 index c84e4e12be..0000000000 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperPluginActivator.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*=================================================================== - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center, -Division of Medical and Biological Informatics. -All rights reserved. - -This software is distributed WITHOUT ANY WARRANTY; without -even the implied warranty of MERCHANTABILITY or FITNESS FOR -A PARTICULAR PURPOSE. - -See LICENSE.txt or http://www.mitk.org for details. - -===================================================================*/ - -#include "mitkImageCropperPluginActivator.h" -#include "QmitkImageCropper.h" - -namespace mitk { - -void ImageCropperPluginActivator::start(ctkPluginContext* context) -{ - BERRY_REGISTER_EXTENSION_CLASS( QmitkImageCropper, context ) -} - -void ImageCropperPluginActivator::stop(ctkPluginContext* context) -{ - Q_UNUSED(context) -} - -} diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/org_mitk_gui_qt_imagecropper_Activator.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/org_mitk_gui_qt_imagecropper_Activator.cpp new file mode 100644 index 0000000000..61f852b699 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/org_mitk_gui_qt_imagecropper_Activator.cpp @@ -0,0 +1,46 @@ +/*========================================================================= + +Program: Medical Imaging & Interaction Toolkit +Language: C++ +Date: $Date$ +Version: $Revision$ + +Copyright (c) German Cancer Research Center, Division of Medical and +Biological Informatics. All rights reserved. +See MITKCopyright.txt or http://www.mitk.org/copyright.html 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 +#include "org_mitk_gui_qt_imagecropper_Activator.h" + +#include + +#include "QmitkImageCropper.h" +#include +#include + +US_INITIALIZE_MODULE + +namespace mitk { + + void org_mitk_gui_qt_imagecropper_Activator::start(ctkPluginContext* context) + { + RegisterBoundingShapeObjectFactory(); + BERRY_REGISTER_EXTENSION_CLASS(QmitkImageCropper, context) + } + + void org_mitk_gui_qt_imagecropper_Activator::stop(ctkPluginContext* context) + { + Q_UNUSED(context) + } + +} + +#if QT_VERSION < QT_VERSION_CHECK(5, 0, 0) +Q_EXPORT_PLUGIN2(org_mitk_gui_qt_imagecropper, mitk::PluginActivator) +#endif \ No newline at end of file diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperPluginActivator.h b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/org_mitk_gui_qt_imagecropper_Activator.h similarity index 68% rename from Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperPluginActivator.h rename to Plugins/org.mitk.gui.qt.imagecropper/src/internal/org_mitk_gui_qt_imagecropper_Activator.h index 670216d71c..b271d44e65 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkImageCropperPluginActivator.h +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/org_mitk_gui_qt_imagecropper_Activator.h @@ -1,39 +1,42 @@ /*=================================================================== The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center, Division of Medical and Biological Informatics. All rights reserved. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See LICENSE.txt or http://www.mitk.org for details. ===================================================================*/ -#ifndef MITKPLUGINACTIVATOR_H -#define MITKPLUGINACTIVATOR_H +#ifndef org_mitk_gui_qt_imagecropper_Activator_h +#define org_mitk_gui_qt_imagecropper_Activator_h #include namespace mitk { -class ImageCropperPluginActivator : + class org_mitk_gui_qt_imagecropper_Activator : public QObject, public ctkPluginActivator { Q_OBJECT - Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_imagecropper") - Q_INTERFACES(ctkPluginActivator) + +#if QT_VERSION >= QT_VERSION_CHECK(5, 0, 0) + Q_PLUGIN_METADATA(IID "org_mitk_gui_qt_imagecropper") +#endif + Q_INTERFACES(ctkPluginActivator) public: void start(ctkPluginContext* context) override; void stop(ctkPluginContext* context) override; }; // PluginActivator } -#endif // MITKPLUGINACTIVATOR_H +#endif // org_mitk_gui_qt_imagecropper_Activator_h