diff --git a/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp index a3765d5512..37bd09a818 100644 --- a/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp +++ b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.cpp @@ -1,326 +1,339 @@ /*============================================================================ 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 // MITK Rendering // VTK #include #include #include #include #include #include #include #include -#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(); auto lookUpTable = localStorage->m_LabelLookupTable->GetVtkLookupTable(); 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 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.05; } 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(); + for (int lidx = 0; lidx < image->GetNumberOfLayers(); ++lidx) + { + localStorage->m_TransferFunctions[lidx] = vtkSmartPointer::New(); + localStorage->m_OpacityTransferFunctions[lidx] = vtkSmartPointer::New(); - localStorage->m_TransferFunction->AddRGBPoint(0, 0., 0., 1.); - localStorage->m_OpacityTransferFunction->AddPoint(0, 0.); + localStorage->m_TransferFunctions[lidx]->AddRGBPoint(0, 0., 0., 1.); + localStorage->m_OpacityTransferFunctions[lidx]->AddPoint(0, 0.); - for (const auto& value : labelValues) - { - double* color = lookUpTable->GetTableValue(value); - localStorage->m_TransferFunction->AddRGBPoint(value, color[0], color[1], color[2]); + for (const auto& value : image->GetLabelValuesByGroup(lidx)) + { + double* color = lookUpTable->GetTableValue(value); + localStorage->m_TransferFunctions[lidx]->AddRGBPoint(value, color[0], color[1], color[2]); - localStorage->m_OpacityTransferFunction->AddPoint(value, color[3]); + localStorage->m_OpacityTransferFunctions[lidx]->AddPoint(value, color[3]); + } } } namespace { std::vector GetOutdatedGroups(const mitk::MultiLabelSegmentationVtkMapper3D::LocalStorage* ls, const mitk::LabelSetImage* seg) { const auto nrOfGroups = seg->GetNumberOfLayers(); std::vector result; for (mitk::LabelSetImage::GroupIndexType groupID = 0; groupID < nrOfGroups; ++groupID) { const auto groupImage = seg->GetGroupImage(groupID); if (groupImage->GetMTime() > ls->m_LastDataUpdateTime || groupImage->GetPipelineMTime() > ls->m_LastDataUpdateTime || ls->m_GroupImageIDs.size() <= groupID || groupImage != ls->m_GroupImageIDs[groupID]) { result.push_back(groupID); } } return result; } } 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); - } - auto outdatedGroups = GetOutdatedGroups(localStorage, image); bool isGeometryModified = (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometryUpdateTime()) || (localStorage->m_LastDataUpdateTime < renderer->GetCurrentWorldPlaneGeometry()->GetMTime()); if (isGeometryModified) { //if geometry is outdated all groups need regeneration outdatedGroups.resize(image->GetNumberOfLayers()); std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0); } if (!outdatedGroups.empty()) { auto hasValidContent = this->GenerateVolumeMapping(renderer, outdatedGroups); if (!hasValidContent) return; } - float opacity = 1.0f; - node->GetOpacity(opacity, renderer, "opacity"); + if (isLookupModified) + { + this->GenerateLookupTable(renderer); + } if (isLookupModified) { //if lookup table is modified all groups need a new color mapping outdatedGroups.resize(image->GetNumberOfLayers()); std::iota(outdatedGroups.begin(), outdatedGroups.end(), 0); } for (const auto groupID : outdatedGroups) { - localStorage->m_LayerVolumeProperties[groupID]->SetColor(localStorage->m_TransferFunction); - localStorage->m_LayerVolumeProperties[groupID]->SetScalarOpacity(localStorage->m_OpacityTransferFunction); - localStorage->m_LayerVolumes[groupID]->SetMapper(localStorage->m_LayerVolumeMappers[groupID]); - localStorage->m_LayerVolumes[groupID]->SetProperty(localStorage->m_LayerVolumeProperties[groupID]); + localStorage->m_LayerVolumes[groupID]->GetProperty()->SetColor(localStorage->m_TransferFunctions[groupID]); + localStorage->m_LayerVolumes[groupID]->GetProperty()->SetScalarOpacity(localStorage->m_OpacityTransferFunctions[groupID]); + localStorage->m_LayerVolumes[groupID]->Update(); } } bool mitk::MultiLabelSegmentationVtkMapper3D::GenerateVolumeMapping(mitk::BaseRenderer* renderer, const std::vector& outdatedGroupIDs) { LocalStorage* localStorage = m_LSH.GetLocalStorage(renderer); mitk::DataNode* node = this->GetDataNode(); auto* image = dynamic_cast(node->GetData()); assert(image && image->IsInitialized()); image->Update(); const auto numberOfLayers = image->GetNumberOfLayers(); if (numberOfLayers != localStorage->m_NumberOfLayers) { if (numberOfLayers > localStorage->m_NumberOfLayers) { for (int lidx = localStorage->m_NumberOfLayers; lidx < numberOfLayers; ++lidx) { localStorage->m_GroupImageIDs.push_back(nullptr); 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_TransferFunctions.push_back(vtkSmartPointer::New()); + localStorage->m_OpacityTransferFunctions.push_back(vtkSmartPointer::New()); } } else { localStorage->m_GroupImageIDs.resize(numberOfLayers); localStorage->m_LayerImages.resize(numberOfLayers); localStorage->m_LayerVolumeMappers.resize(numberOfLayers); - localStorage->m_LayerVolumeProperties.resize(numberOfLayers); localStorage->m_LayerVolumes.resize(numberOfLayers); + + localStorage->m_TransferFunctions.resize(numberOfLayers); + localStorage->m_OpacityTransferFunctions.resize(numberOfLayers); } localStorage->m_NumberOfLayers = numberOfLayers; localStorage->m_Actors = vtkSmartPointer::New(); for (int lidx = 0; lidx < numberOfLayers; ++lidx) { localStorage->m_Actors->AddPart(localStorage->m_LayerVolumes[lidx]); } } for (const auto groupID : outdatedGroupIDs) { const auto groupImage = image->GetGroupImage(groupID); localStorage->m_GroupImageIDs[groupID] = groupImage; localStorage->m_LayerImages[groupID] = groupImage->GetVtkImageData(this->GetTimestep()); + + //need to recreate the volumeMapper because otherwise label data was still rendered even + //if a label was removed. There must be a cleaner way to do it. Exchanging the whole mapper + //is a ugly workaround for now. + localStorage->m_LayerVolumeMappers[groupID] = vtkSmartPointer::New(); + localStorage->m_LayerVolumeMappers[groupID]->SetInputData(localStorage->m_LayerImages[groupID]); - localStorage->m_LayerVolumeProperties[groupID]->ShadeOff(); - localStorage->m_LayerVolumeProperties[groupID]->SetInterpolationTypeToNearest(); + + localStorage->m_LayerVolumes[groupID]->GetProperty()->ShadeOn(); + localStorage->m_LayerVolumes[groupID]->GetProperty()->SetDiffuse(1.0); + localStorage->m_LayerVolumes[groupID]->GetProperty()->SetAmbient(0.4); + localStorage->m_LayerVolumes[groupID]->GetProperty()->SetSpecular(0.2); + localStorage->m_LayerVolumes[groupID]->GetProperty()->SetInterpolationTypeToNearest(); + + localStorage->m_LayerVolumes[groupID]->SetMapper(localStorage->m_LayerVolumeMappers[groupID]); } localStorage->m_LastDataUpdateTime.Modified(); return true; } void mitk::MultiLabelSegmentationVtkMapper3D::Update(mitk::BaseRenderer *renderer) { bool visible = true; + bool has3Dvisualize = true; const DataNode *node = this->GetDataNode(); node->GetVisibility(visible, renderer, "visible"); + node->GetBoolProperty("multilabel.3D.visualize", has3Dvisualize, renderer); 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(); } } 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("labelset.contour.active", BoolProperty::New(true), renderer); - node->SetProperty("labelset.contour.width", FloatProperty::New(2.0), renderer); - Superclass::SetDefaultProperties(node, renderer, overwrite); + + // add/replace the following properties + node->SetProperty("multilabel.3D.visualize", BoolProperty::New(true), renderer); } 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 index 4d9aefeec1..450b8c7e6e 100644 --- a/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.h +++ b/Modules/Multilabel/mitkMultiLabelSegmentationVtkMapper3D.h @@ -1,149 +1,149 @@ /*============================================================================ 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 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; + + std::vector > m_TransferFunctions; + std::vector > m_OpacityTransferFunctions; /** Vector containing the pointer of the currently used group images. * IMPORTANT: This member must not be used to access any data. * Its purpose is to allow checking if the order of the groups has changed * in order to adapt the pipe line accordingly*/ std::vector m_GroupImageIDs; /** \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 destructor 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); protected: /** 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 GenerateVolumeMapping(mitk::BaseRenderer* renderer, const std::vector& outdatedGroupIDs); /** \brief Generates the look up table that should be used. */ void GenerateLookupTable(mitk::BaseRenderer* renderer); }; } // namespace mitk #endif diff --git a/Modules/Multilabel/mitkMultilabelObjectFactory.cpp b/Modules/Multilabel/mitkMultilabelObjectFactory.cpp index eea5b5e3c9..6e0e75742a 100644 --- a/Modules/Multilabel/mitkMultilabelObjectFactory.cpp +++ b/Modules/Multilabel/mitkMultilabelObjectFactory.cpp @@ -1,133 +1,134 @@ /*============================================================================ 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); + mitk::MultiLabelSegmentationVtkMapper3D::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;