diff --git a/Modules/Multilabel/files.cmake b/Modules/Multilabel/files.cmake index 3253bf602b..cc5f29c1e5 100644 --- a/Modules/Multilabel/files.cmake +++ b/Modules/Multilabel/files.cmake @@ -1,22 +1,23 @@ set(CPP_FILES mitkLabel.cpp mitkLabelSetImage.cpp mitkLabelSetImageConverter.cpp mitkLabelSetImageSource.cpp mitkLabelSetImageHelper.cpp mitkLabelSetImageSurfaceStampFilter.cpp mitkLabelSetImageToSurfaceFilter.cpp mitkLabelSetImageToSurfaceThreadedFilter.cpp mitkLabelSetImageVtkMapper2D.cpp mitkMultilabelObjectFactory.cpp mitkMultiLabelIOHelper.cpp mitkMultiLabelEvents.cpp mitkMultiLabelPredicateHelper.cpp mitkDICOMSegmentationPropertyHelper.cpp mitkDICOMSegmentationConstants.cpp mitkSegmentationTaskList.cpp + mitkMultiLabelSegmentationVtkMapper3D.cpp ) set(RESOURCE_FILES ) diff --git a/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp new file mode 100644 index 0000000000..b6983ff6c0 --- /dev/null +++ b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp @@ -0,0 +1,318 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkMultiLabelSegmentationVtkMapper3D.h" + +// MITK +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// MITK Rendering + +// VTK +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +// ITK +#include +#include + +namespace +{ + itk::ModifiedTimeType PropertyTimeStampIsNewer(const mitk::IPropertyProvider* provider, mitk::BaseRenderer* renderer, const std::string& propName, itk::ModifiedTimeType refMT) + { + const std::string context = renderer != nullptr ? renderer->GetName() : ""; + auto prop = provider->GetConstProperty(propName, context); + if (prop != nullptr) + { + return prop->GetTimeStamp() > refMT; + } + return false; + } +} + +mitk::MultiLabelSegmentationVtkMapper3D::MultiLabelSegmentationVtkMapper3D() +{ +} + +mitk::MultiLabelSegmentationVtkMapper3D::~MultiLabelSegmentationVtkMapper3D() +{ +} + +vtkProp *mitk::MultiLabelSegmentationVtkMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) +{ + // return the actor corresponding to the renderer + return m_LSH.GetLocalStorage(renderer)->m_Actors; +} + +mitk::MultiLabelSegmentationVtkMapper3D::LocalStorage *mitk::MultiLabelSegmentationVtkMapper3D::GetLocalStorage( + mitk::BaseRenderer *renderer) +{ + return m_LSH.GetLocalStorage(renderer); +} + +void mitk::MultiLabelSegmentationVtkMapper3D::GenerateLookupTable(mitk::BaseRenderer* renderer) +{ + LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); + mitk::DataNode* node = this->GetDataNode(); + auto* image = dynamic_cast(node->GetData()); + assert(image && image->IsInitialized()); + + localStorage->m_LabelLookupTable = image->GetLookupTable()->Clone(); + const auto labelValues = image->GetAllLabelValues(); + + std::string propertyName = "org.mitk.multilabel.labels.highlighted"; + + mitk::IntVectorProperty::Pointer prop = dynamic_cast(node->GetNonConstProperty(propertyName)); + if (nullptr != prop) + { + const auto highlightedLabelValues = prop->GetValue(); + + if (!highlightedLabelValues.empty()) + { + auto lookUpTable = localStorage->m_LabelLookupTable->GetVtkLookupTable(); + auto highlightEnd = highlightedLabelValues.cend(); + + double rgba[4]; + for (const auto& value : labelValues) + { + lookUpTable->GetTableValue(value, rgba); + if (highlightEnd == std::find(highlightedLabelValues.begin(), highlightedLabelValues.end(), value)) + { //make all none highlighted values more transparent + rgba[3] *= 0.15; + } + else if (rgba[3] != 0) + { //if highlighted values are visible set them to opaque to pop out + rgba[3] = 1.; + } + lookUpTable->SetTableValue(value, rgba); + } + localStorage->m_LabelLookupTable->Modified(); // need to call modified, since LookupTableProperty seems to be unchanged so no widget-update is + // executed + + + localStorage->m_TransferFunction = vtkSmartPointer::New(); + localStorage->m_OpacityTransferFunction = vtkSmartPointer::New(); + + localStorage->m_TransferFunction->AddRGBPoint(0, 0., 0., 1.); + localStorage->m_OpacityTransferFunction->AddPoint(0, 0.); + + for (const auto& value : labelValues) + { + double* color = lookUpTable->GetTableValue(value); + localStorage->m_TransferFunction->AddRGBPoint(value, color[0], color[1], color[2]); + + localStorage->m_OpacityTransferFunction->AddPoint(value, color[3]); + } + } + } +} + +void mitk::MultiLabelSegmentationVtkMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) +{ + LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); + mitk::DataNode *node = this->GetDataNode(); + auto *image = dynamic_cast(node->GetData()); + assert(image && image->IsInitialized()); + + bool isLookupModified = localStorage->m_LabelLookupTable.IsNull() || + (localStorage->m_LabelLookupTable->GetMTime() < image->GetLookupTable()->GetMTime()) || + PropertyTimeStampIsNewer(node, renderer, "org.mitk.multilabel.labels.highlighted", localStorage->m_LabelLookupTable->GetMTime()); + + if (isLookupModified) + { + this->GenerateLookupTable(renderer); + } + + + bool isDataModified = (localStorage->m_LastDataUpdateTime < image->GetMTime()) || + (localStorage->m_LastDataUpdateTime < image->GetPipelineMTime()) || + (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || + (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()); + + if (isDataModified) + { + auto hasValidContent = this->GenerateImageSlice(renderer); + if (!hasValidContent) return; + } + + auto numberOfLayers = image->GetNumberOfLayers(); + + float opacity = 1.0f; + node->GetOpacity(opacity, renderer, "opacity"); + + if (isDataModified && isLookupModified) + { + + } + + for (int lidx = 0; lidx < numberOfLayers; ++lidx) + { + localStorage->m_LayerVolumeProperties[lidx]->SetColor(localStorage->m_TransferFunction); + localStorage->m_LayerVolumeProperties[lidx]->SetScalarOpacity(localStorage->m_OpacityTransferFunction); + localStorage->m_LayerVolumes[lidx]->SetMapper(localStorage->m_LayerVolumeMappers[lidx]); + localStorage->m_LayerVolumes[lidx]->SetProperty(localStorage->m_LayerVolumeProperties[lidx]); + } +} + +bool mitk::MultiLabelSegmentationVtkMapper3D::GenerateImageSlice(mitk::BaseRenderer* renderer) +{ + LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); + mitk::DataNode* node = this->GetDataNode(); + auto* image = dynamic_cast(node->GetData()); + assert(image && image->IsInitialized()); + + image->Update(); + + auto numberOfLayers = image->GetNumberOfLayers(); + + if (numberOfLayers != localStorage->m_NumberOfLayers) + { + localStorage->m_NumberOfLayers = numberOfLayers; + localStorage->m_LayerImages.clear(); + localStorage->m_LayerVolumeMappers.clear(); + localStorage->m_LayerVolumeProperties.clear(); + localStorage->m_LayerVolumes.clear(); + + localStorage->m_Actors = vtkSmartPointer::New(); + + for (int lidx = 0; lidx < numberOfLayers; ++lidx) + { + localStorage->m_LayerImages.push_back(vtkSmartPointer::New()); + localStorage->m_LayerVolumeMappers.push_back(vtkSmartPointer::New()); + localStorage->m_LayerVolumeProperties.push_back(vtkSmartPointer::New()); + localStorage->m_LayerVolumes.push_back(vtkSmartPointer::New()); + + localStorage->m_Actors->AddPart(localStorage->m_LayerVolumes[lidx]); + } + } + + for (int lidx = 0; lidx < numberOfLayers; ++lidx) + { + const auto layerImage = image->GetGroupImage(lidx); + + localStorage->m_LayerImages[lidx] = layerImage->GetVtkImageData(this->GetTimestep()); + localStorage->m_LayerVolumeMappers[lidx]->SetInputData(localStorage->m_LayerImages[lidx]); + localStorage->m_LayerVolumeProperties[lidx]->ShadeOff(); + localStorage->m_LayerVolumeProperties[lidx]->SetInterpolationTypeToNearest(); + } + localStorage->m_LastDataUpdateTime.Modified(); + return true; +} + +void mitk::MultiLabelSegmentationVtkMapper3D::Update(mitk::BaseRenderer *renderer) +{ + bool visible = true; + const DataNode *node = this->GetDataNode(); + node->GetVisibility(visible, renderer, "visible"); + + if (!visible) + return; + + auto *image = dynamic_cast(node->GetData()); + + if (image == nullptr || image->IsInitialized() == false) + return; + + // Calculate time step of the image data for the specified renderer (integer value) + this->CalculateTimeStep(renderer); + + // Check if time step is valid + const TimeGeometry *dataTimeGeometry = image->GetTimeGeometry(); + if ((dataTimeGeometry == nullptr) || (dataTimeGeometry->CountTimeSteps() == 0) || + (!dataTimeGeometry->IsValidTimeStep(this->GetTimestep()))) + { + return; + } + + image->UpdateOutputInformation(); + LocalStorage *localStorage = m_LSH.GetLocalStorage(renderer); + + // check if something important has changed and we need to re-render + + if (localStorage->m_LabelLookupTable.IsNull() || + (localStorage->m_LabelLookupTable->GetMTime() < image->GetLookupTable()->GetMTime()) || + (localStorage->m_LastDataUpdateTime < image->GetMTime()) || + (localStorage->m_LastDataUpdateTime < image->GetPipelineMTime()) || + (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || + (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()) || + (localStorage->m_LastPropertyUpdateTime < node->GetPropertyList()->GetMTime()) || + (localStorage->m_LastPropertyUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || + (localStorage->m_LastPropertyUpdateTime < image->GetPropertyList()->GetMTime())) + { + this->GenerateDataForRenderer(renderer); + localStorage->m_LastPropertyUpdateTime.Modified(); + } + else if ((localStorage->m_LastPropertyUpdateTime < node->GetPropertyList()->GetMTime()) || + (localStorage->m_LastPropertyUpdateTime < node->GetPropertyList(renderer)->GetMTime()) || + (localStorage->m_LastPropertyUpdateTime < image->GetPropertyList()->GetMTime())) + { + this->GenerateDataForRenderer(renderer); + localStorage->m_LastPropertyUpdateTime.Modified(); + } +} + +void mitk::MultiLabelSegmentationVtkMapper3D::SetDefaultProperties(mitk::DataNode *node, + mitk::BaseRenderer *renderer, + bool overwrite) +{ + // add/replace the following properties + node->SetProperty("opacity", FloatProperty::New(1.0f), renderer); + node->SetProperty("binary", BoolProperty::New(false), renderer); + + mitk::RenderingModeProperty::Pointer renderingModeProperty = + mitk::RenderingModeProperty::New(RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); + node->SetProperty("Image Rendering.Mode", renderingModeProperty, renderer); + + mitk::LevelWindow levelwindow(32767.5, 65535); + mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(levelwindow); + + levWinProp->SetLevelWindow(levelwindow); + node->SetProperty("levelwindow", levWinProp, renderer); + + node->SetProperty("labelset.contour.active", BoolProperty::New(true), renderer); + node->SetProperty("labelset.contour.width", FloatProperty::New(2.0), renderer); + + Superclass::SetDefaultProperties(node, renderer, overwrite); +} + +mitk::MultiLabelSegmentationVtkMapper3D::LocalStorage::~LocalStorage() +{ +} + +mitk::MultiLabelSegmentationVtkMapper3D::LocalStorage::LocalStorage() +{ + // Do as much actions as possible in here to avoid double executions. + m_Actors = vtkSmartPointer::New(); + + m_NumberOfLayers = 0; +} diff --git a/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.h b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.h new file mode 100644 index 0000000000..7988717360 --- /dev/null +++ b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.h @@ -0,0 +1,161 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef mitkMultiLabelSegmentationVtkMapper3D_h +#define mitkMultiLabelSegmentationVtkMapper3D_h + +// MITK +#include "MitkMultilabelExports.h" +#include "mitkCommon.h" + +// MITK Rendering +#include "mitkBaseRenderer.h" +#include "mitkExtractSliceFilter.h" +#include "mitkLabelSetImage.h" +#include "mitkVtkMapper.h" + +// VTK +#include + +class vtkPolyDataMapper; +class vtkImageData; +class vtkLookupTable; +class vtkVolumeProperty; +class vtkVolume; +class vtkSmartVolumeMapper; + +namespace mitk +{ + + /** \brief Mapper to resample and display 2D slices of a 3D labelset image. + * + * Properties that can be set for labelset images and influence this mapper are: + * + * - \b "labelset.contour.active": (BoolProperty) whether to show only the active label as a contour or not + * - \b "labelset.contour.width": (FloatProperty) line width of the contour + + * The default properties are: + + * - \b "labelset.contour.active", mitk::BoolProperty::New( true ), renderer, overwrite ) + * - \b "labelset.contour.width", mitk::FloatProperty::New( 2.0 ), renderer, overwrite ) + + * \ingroup Mapper + */ + class MITKMULTILABEL_EXPORT MultiLabelSegmentationVtkMapper3D : public VtkMapper + { + public: + /** Standard class typedefs. */ + mitkClassMacro(MultiLabelSegmentationVtkMapper3D, VtkMapper); + + /** Method for creation through the object factory. */ + itkNewMacro(Self); + + /** \brief Get the Image to map */ + const mitk::Image *GetInput(void); + + /** \brief Checks whether this mapper needs to update itself and generate + * data. */ + void Update(mitk::BaseRenderer *renderer) override; + + //### methods of MITK-VTK rendering pipeline + vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; + //### end of methods of MITK-VTK rendering pipeline + + /** \brief Internal class holding the mapper, actor, etc. for each of the 3 2D render windows */ + /** + * To render axial, coronal, and sagittal, the mapper is called three times. + * For performance reasons, the corresponding data for each view is saved in the + * internal helper class LocalStorage. This allows rendering n views with just + * 1 mitkMapper using n vtkMapper. + * */ + class MITKMULTILABEL_EXPORT LocalStorage : public mitk::Mapper::BaseLocalStorage + { + public: + vtkSmartPointer m_Actors; + + std::vector> m_LayerVolumeMappers; + std::vector> m_LayerImages; + std::vector> m_LayerVolumes; + std::vector> m_LayerVolumeProperties; + + /** \brief Timestamp of last update of stored data. */ + itk::TimeStamp m_LastDataUpdateTime; + /** \brief Timestamp of last update of a property. */ + itk::TimeStamp m_LastPropertyUpdateTime; + + /** look up table for label colors. */ + mitk::LookupTable::Pointer m_LabelLookupTable; + vtkSmartPointer m_TransferFunction; + vtkSmartPointer m_OpacityTransferFunction; + + int m_NumberOfLayers; + + /** \brief Default constructor of the local storage. */ + LocalStorage(); + /** \brief Default deconstructor of the local storage. */ + ~LocalStorage() override; + }; + + /** \brief The LocalStorageHandler holds all (three) LocalStorages for the three 2D render windows. */ + mitk::LocalStorageHandler m_LSH; + + /** \brief Get the LocalStorage corresponding to the current renderer. */ + LocalStorage *GetLocalStorage(mitk::BaseRenderer *renderer); + + /** \brief Set the default properties for general image rendering. */ + static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); + + /** \brief This method switches between different rendering modes (e.g. use a lookup table or a transfer function). + * Detailed documentation about the modes can be found here: \link mitk::RenderingModeProperty \endlink + */ + void ApplyRenderingMode(mitk::BaseRenderer *renderer); + + protected: + /** \brief Transforms the actor to the actual position in 3D. + * \param renderer The current renderer corresponding to the render window. + */ + void TransformActor(mitk::BaseRenderer *renderer); + + /** Default constructor */ + MultiLabelSegmentationVtkMapper3D(); + /** Default deconstructor */ + ~MultiLabelSegmentationVtkMapper3D() override; + + /** \brief Does the actual resampling, without rendering the image yet. + * All the data is generated inside this method. The vtkProp (or Actor) + * is filled with content (i.e. the resliced image). + * + * After generation, a 4x4 transformation matrix(t) of the current slice is obtained + * from the vtkResliceImage object via GetReslicesAxis(). This matrix is + * applied to each textured plane (actor->SetUserTransform(t)) to transform everything + * to the actual 3D position (cf. the following image). + * + * \image html cameraPositioning3D.png + * + */ + void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; + + bool GenerateImageSlice(mitk::BaseRenderer* renderer); + + /** \brief Generates the look up table that should be used. + */ + void GenerateLookupTable(mitk::BaseRenderer* renderer); + + /** \brief This method uses the vtkCamera clipping range and the layer property + * to calcualte the depth of the object (e.g. image or contour). The depth is used + * to keep the correct order for the final VTK rendering.*/ + float CalculateLayerDepth(mitk::BaseRenderer *renderer); + }; + +} // namespace mitk + +#endif diff --git a/Modules/Multilabel/mitkMultilabelObjectFactory.cpp b/Modules/Multilabel/mitkMultilabelObjectFactory.cpp index 03be75a0e8..eea5b5e3c9 100644 --- a/Modules/Multilabel/mitkMultilabelObjectFactory.cpp +++ b/Modules/Multilabel/mitkMultilabelObjectFactory.cpp @@ -1,124 +1,133 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkMultilabelObjectFactory.h" #include "mitkBaseRenderer.h" #include "mitkCoreObjectFactory.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include #include #include +#include #include mitk::MultilabelObjectFactory::MultilabelObjectFactory() : CoreObjectFactoryBase() { static bool alreadyDone = false; if (!alreadyDone) { MITK_DEBUG << "MultilabelObjectFactory c'tor" << std::endl; CreateFileExtensionsMap(); alreadyDone = true; } } mitk::MultilabelObjectFactory::~MultilabelObjectFactory() { } mitk::Mapper::Pointer mitk::MultilabelObjectFactory::CreateMapper(mitk::DataNode *node, MapperSlotId id) { mitk::Mapper::Pointer newMapper = nullptr; mitk::BaseData *data = node->GetData(); if (id == mitk::BaseRenderer::Standard2D) { if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::LabelSetImageVtkMapper2D::New(); newMapper->SetDataNode(node); } } + else if (id == mitk::BaseRenderer::Standard3D) + { + if ((dynamic_cast(data) != nullptr)) + { + newMapper = mitk::MultiLabelSegmentationVtkMapper3D::New(); + newMapper->SetDataNode(node); + } + } return newMapper; } void mitk::MultilabelObjectFactory::SetDefaultProperties(mitk::DataNode *node) { if (node == nullptr) return; if (node->GetData() == nullptr) return; if (dynamic_cast(node->GetData()) != nullptr) { mitk::LabelSetImageVtkMapper2D::SetDefaultProperties(node); auto propertyFilters = CoreServices::GetPropertyFilters(); if (propertyFilters != nullptr) { PropertyFilter labelSetImageFilter; labelSetImageFilter.AddEntry("binaryimage.hoveringannotationcolor", PropertyFilter::Blacklist); labelSetImageFilter.AddEntry("binaryimage.hoveringcolor", PropertyFilter::Blacklist); labelSetImageFilter.AddEntry("binaryimage.selectedannotationcolor", PropertyFilter::Blacklist); labelSetImageFilter.AddEntry("binaryimage.selectedcolor", PropertyFilter::Blacklist); labelSetImageFilter.AddEntry("outline binary shadow color", PropertyFilter::Blacklist); propertyFilters->AddFilter(labelSetImageFilter, "LabelSetImage"); } } } std::string mitk::MultilabelObjectFactory::GetFileExtensions() { std::string fileExtension; this->CreateFileExtensions({}, fileExtension); return fileExtension.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::MultilabelObjectFactory::GetFileExtensionsMap() { return {}; } mitk::CoreObjectFactoryBase::MultimapType mitk::MultilabelObjectFactory::GetSaveFileExtensionsMap() { return {}; } void mitk::MultilabelObjectFactory::CreateFileExtensionsMap() { } std::string mitk::MultilabelObjectFactory::GetSaveFileExtensions() { std::string fileExtension; this->CreateFileExtensions({}, fileExtension); return fileExtension.c_str(); } struct RegisterMultilabelObjectFactory { RegisterMultilabelObjectFactory() : m_Factory(mitk::MultilabelObjectFactory::New()) { mitk::CoreObjectFactory::GetInstance()->RegisterExtraFactory(m_Factory); } ~RegisterMultilabelObjectFactory() { mitk::CoreObjectFactory::GetInstance()->UnRegisterExtraFactory(m_Factory); } mitk::MultilabelObjectFactory::Pointer m_Factory; }; static RegisterMultilabelObjectFactory registerMultilabelObjectFactory;