From 82e5adaab84f7c064bb5de1779877a9692ff159a Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Tue, 10 Oct 2023 10:26:03 +0200 Subject: [PATCH 01/92] Add boilerplate code to new ROI and ROIIO modules --- Modules/ModuleList.cmake | 1 + Modules/ROI/CMakeLists.txt | 5 + Modules/ROI/autoload/IO/CMakeLists.txt | 5 + Modules/ROI/autoload/IO/files.cmake | 13 +++ .../autoload/IO/include/mitkROIIOMimeTypes.h | 38 ++++++ Modules/ROI/autoload/IO/src/mitkROIIO.cpp | 36 ++++++ Modules/ROI/autoload/IO/src/mitkROIIO.h | 36 ++++++ .../autoload/IO/src/mitkROIIOMimeTypes.cpp | 75 ++++++++++++ .../IO/src/mitkROIIOModuleActivator.cpp | 37 ++++++ .../IO/src/mitkROIIOModuleActivator.h | 35 ++++++ .../ROI/autoload/IO/src/mitkROISerializer.cpp | 55 +++++++++ .../ROI/autoload/IO/src/mitkROISerializer.h | 34 ++++++ Modules/ROI/files.cmake | 13 +++ Modules/ROI/include/mitkROI.h | 42 +++++++ Modules/ROI/include/mitkROIMapper2D.h | 56 +++++++++ Modules/ROI/include/mitkROIMapper3D.h | 56 +++++++++ Modules/ROI/src/mitkROI.cpp | 44 +++++++ Modules/ROI/src/mitkROIMapper2D.cpp | 45 ++++++++ Modules/ROI/src/mitkROIMapper3D.cpp | 45 ++++++++ Modules/ROI/src/mitkROIObjectFactory.cpp | 108 ++++++++++++++++++ Modules/ROI/src/mitkROIObjectFactory.h | 39 +++++++ 21 files changed, 818 insertions(+) create mode 100644 Modules/ROI/CMakeLists.txt create mode 100644 Modules/ROI/autoload/IO/CMakeLists.txt create mode 100644 Modules/ROI/autoload/IO/files.cmake create mode 100644 Modules/ROI/autoload/IO/include/mitkROIIOMimeTypes.h create mode 100644 Modules/ROI/autoload/IO/src/mitkROIIO.cpp create mode 100644 Modules/ROI/autoload/IO/src/mitkROIIO.h create mode 100644 Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp create mode 100644 Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.cpp create mode 100644 Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h create mode 100644 Modules/ROI/autoload/IO/src/mitkROISerializer.cpp create mode 100644 Modules/ROI/autoload/IO/src/mitkROISerializer.h create mode 100644 Modules/ROI/files.cmake create mode 100644 Modules/ROI/include/mitkROI.h create mode 100644 Modules/ROI/include/mitkROIMapper2D.h create mode 100644 Modules/ROI/include/mitkROIMapper3D.h create mode 100644 Modules/ROI/src/mitkROI.cpp create mode 100644 Modules/ROI/src/mitkROIMapper2D.cpp create mode 100644 Modules/ROI/src/mitkROIMapper3D.cpp create mode 100644 Modules/ROI/src/mitkROIObjectFactory.cpp create mode 100644 Modules/ROI/src/mitkROIObjectFactory.h diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 89d05884657..b027952284a 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -74,4 +74,5 @@ set(MITK_MODULES REST RESTService DICOMweb + ROI ) diff --git a/Modules/ROI/CMakeLists.txt b/Modules/ROI/CMakeLists.txt new file mode 100644 index 00000000000..147dcf0fcfc --- /dev/null +++ b/Modules/ROI/CMakeLists.txt @@ -0,0 +1,5 @@ +mitk_create_module( + DEPENDS MitkCore +) + +add_subdirectory(autoload/IO) diff --git a/Modules/ROI/autoload/IO/CMakeLists.txt b/Modules/ROI/autoload/IO/CMakeLists.txt new file mode 100644 index 00000000000..984bfe1eaae --- /dev/null +++ b/Modules/ROI/autoload/IO/CMakeLists.txt @@ -0,0 +1,5 @@ +mitk_create_module(ROIIO + DEPENDS PUBLIC MitkROI MitkSceneSerialization + PACKAGE_DEPENDS PRIVATE nlohmann_json + AUTOLOAD_WITH MitkCore +) diff --git a/Modules/ROI/autoload/IO/files.cmake b/Modules/ROI/autoload/IO/files.cmake new file mode 100644 index 00000000000..cd11c12ddf6 --- /dev/null +++ b/Modules/ROI/autoload/IO/files.cmake @@ -0,0 +1,13 @@ +set(H_FILES + include/mitkROIIOMimeTypes.h + src/mitkROIIO.h + src/mitkROIIOModuleActivator.h + src/mitkROISerializer.h +) + +set(CPP_FILES + mitkROIIO.cpp + mitkROIIOMimeTypes.cpp + mitkROIIOModuleActivator.cpp + mitkROISerializer.cpp +) diff --git a/Modules/ROI/autoload/IO/include/mitkROIIOMimeTypes.h b/Modules/ROI/autoload/IO/include/mitkROIIOMimeTypes.h new file mode 100644 index 00000000000..75bdad237e4 --- /dev/null +++ b/Modules/ROI/autoload/IO/include/mitkROIIOMimeTypes.h @@ -0,0 +1,38 @@ +/*============================================================================ + +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 mitkROIIOMimeTypes_h +#define mitkROIIOMimeTypes_h + +#include +#include + +namespace mitk +{ + namespace MitkROIIOMimeTypes + { + class MITKROIIO_EXPORT MitkROIMimeType : public CustomMimeType + { + public: + MitkROIMimeType(); + + bool AppliesTo(const std::string& path) const override; + MitkROIMimeType* Clone() const override; + }; + + MITKROIIO_EXPORT MitkROIMimeType ROI_MIMETYPE(); + MITKROIIO_EXPORT std::string ROI_MIMETYPE_NAME(); + MITKROIIO_EXPORT std::vector Get(); + } +} + +#endif diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp new file mode 100644 index 00000000000..0354b844dc1 --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -0,0 +1,36 @@ +/*============================================================================ + +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 "mitkROIIO.h" +#include +#include + +mitk::ROIIO::ROIIO() + : AbstractFileIO(ROI::GetStaticNameOfClass(), MitkROIIOMimeTypes::ROI_MIMETYPE(), "MITK ROI") +{ + this->RegisterService(); +} + +std::vector mitk::ROIIO::DoRead() +{ + std::vector result; + return result; +} + +void mitk::ROIIO::Write() +{ +} + +mitk::ROIIO* mitk::ROIIO::IOClone() const +{ + return new ROIIO(*this); +} diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.h b/Modules/ROI/autoload/IO/src/mitkROIIO.h new file mode 100644 index 00000000000..dee6434802c --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.h @@ -0,0 +1,36 @@ +/*============================================================================ + +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 mitkROIIO_h +#define mitkROIIO_h + +#include + +namespace mitk +{ + class ROIIO : public AbstractFileIO + { + public: + ROIIO(); + + using AbstractFileReader::Read; + void Write() override; + + protected: + std::vector DoRead() override; + + private: + ROIIO* IOClone() const override; + }; +} + +#endif diff --git a/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp b/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp new file mode 100644 index 00000000000..91b8032fe57 --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIOMimeTypes.cpp @@ -0,0 +1,75 @@ +/*============================================================================ + +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 +#include + +#include +#include + +#include + +mitk::MitkROIIOMimeTypes::MitkROIMimeType::MitkROIMimeType() + : CustomMimeType(ROI_MIMETYPE_NAME()) +{ + this->AddExtension("json"); + this->SetCategory("MITK ROI"); + this->SetComment("MITK ROI"); +} + +bool mitk::MitkROIIOMimeTypes::MitkROIMimeType::AppliesTo(const std::string& path) const +{ + bool result = CustomMimeType::AppliesTo(path); + + if (!std::filesystem::exists(path)) // T18572 + return result; + + std::ifstream file(path); + + if (!file.is_open()) + return false; + + auto json = nlohmann::json::parse(file, nullptr, false); + + if (json.is_discarded() || !json.is_object()) + return false; + + if ("MITK ROI" != json.value("FileFormat", "")) + return false; + + if (1 != json.value("Version", 0)) + return false; + + return true; +} + +mitk::MitkROIIOMimeTypes::MitkROIMimeType* mitk::MitkROIIOMimeTypes::MitkROIMimeType::Clone() const +{ + return new MitkROIMimeType(*this); +} + +mitk::MitkROIIOMimeTypes::MitkROIMimeType mitk::MitkROIIOMimeTypes::ROI_MIMETYPE() +{ + return MitkROIMimeType(); +} + +std::string mitk::MitkROIIOMimeTypes::ROI_MIMETYPE_NAME() +{ + return IOMimeTypes::DEFAULT_BASE_NAME() + ".roi"; +} + +std::vector mitk::MitkROIIOMimeTypes::Get() +{ + std::vector mimeTypes; + mimeTypes.push_back(ROI_MIMETYPE().Clone()); + return mimeTypes; +} diff --git a/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.cpp b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.cpp new file mode 100644 index 00000000000..2c9304f0045 --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.cpp @@ -0,0 +1,37 @@ +/*============================================================================ + +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 "mitkROIIOModuleActivator.h" + +#include +#include "mitkROIIO.h" + +#include + +US_EXPORT_MODULE_ACTIVATOR(mitk::ROIIOModuleActivator) + +void mitk::ROIIOModuleActivator::Load(us::ModuleContext* context) +{ + auto mimeTypes = MitkROIIOMimeTypes::Get(); + + us::ServiceProperties props; + props[us::ServiceConstants::SERVICE_RANKING()] = 10; + + for (auto mimeType : mimeTypes) + context->RegisterService(mimeType, props); + + m_FileIOs.push_back(std::make_shared()); +} + +void mitk::ROIIOModuleActivator::Unload(us::ModuleContext*) +{ +} diff --git a/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h new file mode 100644 index 00000000000..d5412c1c7fc --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h @@ -0,0 +1,35 @@ +/*============================================================================ + +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 +#include + +namespace mitk +{ + class AbstractFileIO; + + class ROIIOModuleActivator : public us::ModuleActivator + { + public: + ROIIOModuleActivator() = default; + ~ROIIOModuleActivator() override = default; + + ROIIOModuleActivator(const ROIIOModuleActivator&) = delete; + ROIIOModuleActivator& operator=(const ROIIOModuleActivator&) = delete; + + void Load(us::ModuleContext* context) override; + void Unload(us::ModuleContext*) override; + + private: + std::vector> m_FileIOs; + }; +} diff --git a/Modules/ROI/autoload/IO/src/mitkROISerializer.cpp b/Modules/ROI/autoload/IO/src/mitkROISerializer.cpp new file mode 100644 index 00000000000..6148f54324b --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROISerializer.cpp @@ -0,0 +1,55 @@ +/*============================================================================ + +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 "mitkROISerializer.h" + +#include +#include + +MITK_REGISTER_SERIALIZER(ROISerializer) + +mitk::ROISerializer::ROISerializer() +{ +} + +mitk::ROISerializer::~ROISerializer() +{ +} + +std::string mitk::ROISerializer::Serialize() +{ + auto roi = dynamic_cast(m_Data.GetPointer()); + + if (nullptr == roi) + { + MITK_ERROR << "Object at " << (const void*)this->m_Data << " is not an mitk::ROI. Cannot serialize as MITK ROI."; + return ""; + } + + auto filename = this->GetUniqueFilenameInWorkingDirectory(); + filename += "_" + m_FilenameHint + ".json"; + + std::string path = m_WorkingDirectory; + path += IOUtil::GetDirectorySeparator() + filename; + + try + { + IOUtil::Save(roi, path); + } + catch (std::exception& e) + { + MITK_ERROR << "Error serializing object at " << (const void*)this->m_Data << " to " << path << ": " << e.what(); + return ""; + } + + return filename; +} diff --git a/Modules/ROI/autoload/IO/src/mitkROISerializer.h b/Modules/ROI/autoload/IO/src/mitkROISerializer.h new file mode 100644 index 00000000000..0daf876a017 --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROISerializer.h @@ -0,0 +1,34 @@ +/*============================================================================ + +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 mitkROISerializer_h +#define mitkROISerializer_h + +#include + +namespace mitk +{ + class ROISerializer : public BaseDataSerializer + { + public: + mitkClassMacro(ROISerializer, BaseDataSerializer) + itkFactorylessNewMacro(Self) + + std::string Serialize() override; + + protected: + ROISerializer(); + ~ROISerializer() override; + }; +} + +#endif diff --git a/Modules/ROI/files.cmake b/Modules/ROI/files.cmake new file mode 100644 index 00000000000..8526fe310ae --- /dev/null +++ b/Modules/ROI/files.cmake @@ -0,0 +1,13 @@ +set(H_FILES + include/mitkROI.h + include/mitkROIMapper2D.h + include/mitkROIMapper3D.h + src/mitkROIObjectFactory.h +) + +set(CPP_FILES + mitkROI.cpp + mitkROIMapper2D.cpp + mitkROIMapper3D.cpp + mitkROIObjectFactory.cpp +) diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h new file mode 100644 index 00000000000..8167f78cb96 --- /dev/null +++ b/Modules/ROI/include/mitkROI.h @@ -0,0 +1,42 @@ +/*============================================================================ + +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 mitkROI_h +#define mitkROI_h + +#include +#include + +namespace mitk +{ + class MITKROI_EXPORT ROI : public BaseData + { + public: + mitkClassMacro(ROI, BaseData) + itkFactorylessNewMacro(Self) + itkCloneMacro(Self) + + void SetRequestedRegionToLargestPossibleRegion() override; + bool RequestedRegionIsOutsideOfTheBufferedRegion() override; + bool VerifyRequestedRegion() override; + void SetRequestedRegion(const itk::DataObject* data) override; + + protected: + mitkCloneMacro(Self) + + ROI(); + ROI(const Self& other); + ~ROI() override; + }; +} + +#endif diff --git a/Modules/ROI/include/mitkROIMapper2D.h b/Modules/ROI/include/mitkROIMapper2D.h new file mode 100644 index 00000000000..430302e7619 --- /dev/null +++ b/Modules/ROI/include/mitkROIMapper2D.h @@ -0,0 +1,56 @@ +/*============================================================================ + +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 mitkROIMapper2D_h +#define mitkROIMapper2D_h + +#include +#include +#include + +#include +#include + +namespace mitk +{ + class MITKROI_EXPORT ROIMapper2D : public VtkMapper + { + class LocalStorage : public Mapper::BaseLocalStorage + { + public: + LocalStorage(); + ~LocalStorage() override; + + vtkPropAssembly* GetPropAssembly() const; + + protected: + vtkSmartPointer m_PropAssembly; + }; + + public: + static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool override = false); + + mitkClassMacro(ROIMapper2D, VtkMapper) + itkFactorylessNewMacro(Self) + + vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; + + protected: + ROIMapper2D(); + ~ROIMapper2D() override; + + private: + LocalStorageHandler m_LocalStorageHandler; + }; +} + +#endif diff --git a/Modules/ROI/include/mitkROIMapper3D.h b/Modules/ROI/include/mitkROIMapper3D.h new file mode 100644 index 00000000000..91a8a65918b --- /dev/null +++ b/Modules/ROI/include/mitkROIMapper3D.h @@ -0,0 +1,56 @@ +/*============================================================================ + +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 mitkROIMapper3D_h +#define mitkROIMapper3D_h + +#include +#include +#include + +#include +#include + +namespace mitk +{ + class MITKROI_EXPORT ROIMapper3D : public VtkMapper + { + class LocalStorage : public Mapper::BaseLocalStorage + { + public: + LocalStorage(); + ~LocalStorage() override; + + vtkPropAssembly* GetPropAssembly() const; + + protected: + vtkSmartPointer m_PropAssembly; + }; + + public: + static void SetDefaultProperties(DataNode* node, BaseRenderer* renderer = nullptr, bool override = false); + + mitkClassMacro(ROIMapper3D, VtkMapper) + itkFactorylessNewMacro(Self) + + vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; + + protected: + ROIMapper3D(); + ~ROIMapper3D() override; + + private: + LocalStorageHandler m_LocalStorageHandler; + }; +} + +#endif diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp new file mode 100644 index 00000000000..91b5d905e2a --- /dev/null +++ b/Modules/ROI/src/mitkROI.cpp @@ -0,0 +1,44 @@ +/*============================================================================ + +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 + +mitk::ROI::ROI() +{ +} + +mitk::ROI::ROI(const Self& other) + : BaseData(other) +{ +} + +mitk::ROI::~ROI() +{ +} + +void mitk::ROI::SetRequestedRegionToLargestPossibleRegion() +{ +} + +bool mitk::ROI::RequestedRegionIsOutsideOfTheBufferedRegion() +{ + return false; +} + +bool mitk::ROI::VerifyRequestedRegion() +{ + return true; +} + +void mitk::ROI::SetRequestedRegion(const itk::DataObject* data) +{ +} diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp new file mode 100644 index 00000000000..c2adedc3fc0 --- /dev/null +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -0,0 +1,45 @@ +/*============================================================================ + +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 + +mitk::ROIMapper2D::LocalStorage::LocalStorage() + : m_PropAssembly(vtkSmartPointer::New()) +{ +} + +mitk::ROIMapper2D::LocalStorage::~LocalStorage() +{ +} + +vtkPropAssembly* mitk::ROIMapper2D::LocalStorage::GetPropAssembly() const +{ + return m_PropAssembly; +} + +void mitk::ROIMapper2D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) +{ + Superclass::SetDefaultProperties(node, renderer, override); +} + +mitk::ROIMapper2D::ROIMapper2D() +{ +} + +mitk::ROIMapper2D::~ROIMapper2D() +{ +} + +vtkProp* mitk::ROIMapper2D::GetVtkProp(BaseRenderer* renderer) +{ + return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); +} diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp new file mode 100644 index 00000000000..c3ff36625b6 --- /dev/null +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -0,0 +1,45 @@ +/*============================================================================ + +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 + +mitk::ROIMapper3D::LocalStorage::LocalStorage() + : m_PropAssembly(vtkSmartPointer::New()) +{ +} + +mitk::ROIMapper3D::LocalStorage::~LocalStorage() +{ +} + +vtkPropAssembly* mitk::ROIMapper3D::LocalStorage::GetPropAssembly() const +{ + return m_PropAssembly; +} + +void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) +{ + Superclass::SetDefaultProperties(node, renderer, override); +} + +mitk::ROIMapper3D::ROIMapper3D() +{ +} + +mitk::ROIMapper3D::~ROIMapper3D() +{ +} + +vtkProp* mitk::ROIMapper3D::GetVtkProp(BaseRenderer* renderer) +{ + return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); +} diff --git a/Modules/ROI/src/mitkROIObjectFactory.cpp b/Modules/ROI/src/mitkROIObjectFactory.cpp new file mode 100644 index 00000000000..cffe1fca6d5 --- /dev/null +++ b/Modules/ROI/src/mitkROIObjectFactory.cpp @@ -0,0 +1,108 @@ +/*============================================================================ + +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 "mitkROIObjectFactory.h" +#include +#include +#include +#include + +mitk::ROIObjectFactory::ROIObjectFactory() +{ +} + +mitk::ROIObjectFactory::~ROIObjectFactory() +{ +} + +mitk::Mapper::Pointer mitk::ROIObjectFactory::CreateMapper(DataNode* node, MapperSlotId slotId) +{ + Mapper::Pointer mapper; + + if (node != nullptr) + { + auto* roi = dynamic_cast(node->GetData()); + + if (roi != nullptr) + { + if (slotId == BaseRenderer::Standard2D) + { + mapper = ROIMapper2D::New(); + } + else if (slotId == BaseRenderer::Standard3D) + { + mapper = ROIMapper3D::New(); + } + + if (mapper.IsNotNull()) + mapper->SetDataNode(node); + } + } + + return mapper; +} + +void mitk::ROIObjectFactory::SetDefaultProperties(DataNode* node) +{ + if (node == nullptr) + return; + + auto* roi = dynamic_cast(node->GetData()); + + if (roi == nullptr) + return; + + ROIMapper2D::SetDefaultProperties(node); + ROIMapper3D::SetDefaultProperties(node); +} + +std::string mitk::ROIObjectFactory::GetFileExtensions() +{ + return ""; +} + +mitk::ROIObjectFactory::MultimapType mitk::ROIObjectFactory::GetFileExtensionsMap() +{ + return {}; +} + +std::string mitk::ROIObjectFactory::GetSaveFileExtensions() +{ + return ""; +} + +mitk::ROIObjectFactory::MultimapType mitk::ROIObjectFactory::GetSaveFileExtensionsMap() +{ + return {}; +} + +namespace mitk +{ + class RegisterROIObjectFactory + { + public: + RegisterROIObjectFactory() + : m_Factory(ROIObjectFactory::New()) + { + CoreObjectFactory::GetInstance()->RegisterExtraFactory(m_Factory); + } + + ~RegisterROIObjectFactory() + { + } + + private: + ROIObjectFactory::Pointer m_Factory; + }; +} + +static mitk::RegisterROIObjectFactory registerROIObjectFactory; diff --git a/Modules/ROI/src/mitkROIObjectFactory.h b/Modules/ROI/src/mitkROIObjectFactory.h new file mode 100644 index 00000000000..75ac43fc92f --- /dev/null +++ b/Modules/ROI/src/mitkROIObjectFactory.h @@ -0,0 +1,39 @@ +/*============================================================================ + +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 mitkROIObjectFactory_h +#define mitkROIObjectFactory_h + +#include + +namespace mitk +{ + class ROIObjectFactory : public CoreObjectFactoryBase + { + public: + mitkClassMacro(ROIObjectFactory, CoreObjectFactoryBase) + itkFactorylessNewMacro(Self) + + Mapper::Pointer CreateMapper(DataNode* node, MapperSlotId slotId) override; + void SetDefaultProperties(DataNode *node) override; + std::string GetFileExtensions() override; + MultimapType GetFileExtensionsMap() override; + std::string GetSaveFileExtensions() override; + MultimapType GetSaveFileExtensionsMap() override; + + protected: + ROIObjectFactory(); + ~ROIObjectFactory() override; + }; +} + +#endif From 26f565fbefb55b762ed17138418dc97c8cda0518 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 18 Oct 2023 01:05:39 +0200 Subject: [PATCH 02/92] Add ROI icon --- Modules/QtWidgets/resource/Qmitk.qrc | 1 + Modules/QtWidgets/resource/ROIIcon.svg | 15 +++++++++++++++ .../QtWidgets/src/QmitkNodeDescriptorManager.cpp | 4 ++++ 3 files changed, 20 insertions(+) create mode 100644 Modules/QtWidgets/resource/ROIIcon.svg diff --git a/Modules/QtWidgets/resource/Qmitk.qrc b/Modules/QtWidgets/resource/Qmitk.qrc index 377d75c396e..e2c136c4b7d 100644 --- a/Modules/QtWidgets/resource/Qmitk.qrc +++ b/Modules/QtWidgets/resource/Qmitk.qrc @@ -30,5 +30,6 @@ invisible.svg visible.svg SegmentationTaskListIcon.svg + ROIIcon.svg diff --git a/Modules/QtWidgets/resource/ROIIcon.svg b/Modules/QtWidgets/resource/ROIIcon.svg new file mode 100644 index 00000000000..dc1421047b7 --- /dev/null +++ b/Modules/QtWidgets/resource/ROIIcon.svg @@ -0,0 +1,15 @@ + + + + + +image/svg+xml + + + + + + + + + diff --git a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp index bd279a298af..8e6fbaa8874 100644 --- a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp +++ b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp @@ -46,6 +46,10 @@ void QmitkNodeDescriptorManager::Initialize() auto isSegmentationTaskList = mitk::NodePredicateDataType::New("SegmentationTaskList"); AddDescriptor(new QmitkNodeDescriptor("SegmentationTaskList", segmentationTaskListIcon, isSegmentationTaskList, this)); + auto roiIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/ROIIcon.svg")); + auto isROI = mitk::NodePredicateDataType::New("ROI"); + AddDescriptor(new QmitkNodeDescriptor("ROI", roiIcon, isROI, this)); + auto isPointSet = mitk::NodePredicateDataType::New("PointSet"); AddDescriptor(new QmitkNodeDescriptor(tr("PointSet"), QString(":/Qmitk/PointSet_48.png"), isPointSet, this)); From 38dfa4cc1dd66d60c0b91cf8d876ef51cf8080fb Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 18 Oct 2023 01:06:24 +0200 Subject: [PATCH 03/92] Start IO implementation for ROIs --- Modules/Core/include/mitkPoint.h | 24 ++++++- Modules/Core/include/mitkVector.h | 26 +++++-- Modules/ROI/autoload/IO/src/mitkROIIO.cpp | 86 ++++++++++++++++++++++- 3 files changed, 127 insertions(+), 9 deletions(-) diff --git a/Modules/Core/include/mitkPoint.h b/Modules/Core/include/mitkPoint.h index 0436d5ae23a..f8844cbae80 100644 --- a/Modules/Core/include/mitkPoint.h +++ b/Modules/Core/include/mitkPoint.h @@ -15,9 +15,11 @@ found in the LICENSE file. #include -#include "mitkArray.h" -#include "mitkEqual.h" -#include "mitkNumericConstants.h" +#include +#include +#include + +#include namespace mitk { @@ -91,6 +93,22 @@ namespace mitk } }; + template + void to_json(nlohmann::json& j, const Point& p) + { + j = nlohmann::json::array(); + + for (size_t i = 0; i < NPointDimension; ++i) + j.push_back(p[i]); + } + + template + void from_json(const nlohmann::json& j, Point& p) + { + for (size_t i = 0; i < NPointDimension; ++i) + p[i] = j.at(i).get(); + } + typedef Point Point2D; typedef Point Point3D; typedef Point Point4D; diff --git a/Modules/Core/include/mitkVector.h b/Modules/Core/include/mitkVector.h index fca4c9047d6..ac111e18ee2 100644 --- a/Modules/Core/include/mitkVector.h +++ b/Modules/Core/include/mitkVector.h @@ -17,10 +17,12 @@ found in the LICENSE file. #include #include -#include "mitkArray.h" -#include "mitkEqual.h" -#include "mitkExceptionMacro.h" -#include "mitkNumericConstants.h" +#include +#include +#include +#include + +#include namespace mitk { @@ -131,6 +133,22 @@ namespace mitk operator vnl_vector() const { return this->GetVnlVector(); } }; // end mitk::Vector + template + void to_json(nlohmann::json &j, const Vector &v) + { + j = nlohmann::json::array(); + + for (size_t i = 0; i < NVectorDimension; ++i) + j.push_back(v[i]); + } + + template + void from_json(const nlohmann::json &j, Vector &v) + { + for (size_t i = 0; i < NVectorDimension; ++i) + v[i] = j.at(i).get(); + } + // convenience typedefs for often used mitk::Vector representations. typedef Vector Vector2D; diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp index 0354b844dc1..6bdce335bbd 100644 --- a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -14,6 +14,11 @@ found in the LICENSE file. #include #include +#include + +#include +#include + mitk::ROIIO::ROIIO() : AbstractFileIO(ROI::GetStaticNameOfClass(), MitkROIIOMimeTypes::ROI_MIMETYPE(), "MITK ROI") { @@ -22,8 +27,85 @@ mitk::ROIIO::ROIIO() std::vector mitk::ROIIO::DoRead() { - std::vector result; - return result; + auto *stream = this->GetInputStream(); + std::ifstream fileStream; + + if (nullptr == stream) + { + auto filename = this->GetInputLocation(); + + if (filename.empty() || !std::filesystem::exists(filename)) + mitkThrow() << "Invalid or nonexistent filename: \"" << filename << "\"!"; + + fileStream.open(filename); + + if (!fileStream.is_open()) + mitkThrow() << "Could not open file \"" << filename << "\" for reading!"; + + stream = &fileStream; + } + + nlohmann::json json; + + try + { + json = nlohmann::json::parse(*stream); + } + catch (const nlohmann::json::exception &e) + { + mitkThrow() << e.what(); + } + + if (!json.is_object()) + mitkThrow() << "Unknown file format (expected JSON object as root)!"; + + if ("MITK ROI" != json.value("FileFormat", "")) + mitkThrow() << "Unknown file format (expected \"MITK ROI\")!"; + + if (1 != json.value("Version", 0)) + mitkThrow() << "Unknown file format version (expected \"1\")!"; + + auto geometry = Geometry3D::New(); + + if (json.contains("Geometry")) + { + auto jsonGeometry = json["Geometry"]; + + if (!jsonGeometry.is_object()) + mitkThrow() << "Geometry is expected to be a JSON object."; + + auto geometryType = jsonGeometry.value("Type", "Embedded"); + + if (geometryType != "Embedded") + mitkThrow() << "Unknown geometry type \"" << geometryType << "\"!"; + + if (jsonGeometry.contains("Origin")) + geometry->SetOrigin(jsonGeometry["Origin"].get()); + + if (jsonGeometry.contains("Spacing")) + geometry->SetSpacing(jsonGeometry["Spacing"].get()); + } + + if (!json.contains("ROIs") || !json["ROIs"].is_array()) + mitkThrow() << "ROIs array not found!"; + + std::vector results; + + try + { + for (const auto& roi : json["ROIs"]) + { + auto result = ROI::New(); + result->SetGeometry(geometry); + results.push_back(result.GetPointer()); + } + } + catch (const nlohmann::json::type_error &e) + { + mitkThrow() << e.what(); + } + + return results; } void mitk::ROIIO::Write() From ac98a8eaba1b032de3f1bf712c1738af669a3f9b Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 18 Oct 2023 01:09:01 +0200 Subject: [PATCH 04/92] Start implementation of property serialization in JSON --- Modules/Core/include/mitkBaseProperty.h | 4 + Modules/Core/include/mitkColorProperty.h | 22 ++++++ .../Core/include/mitkEnumerationProperty.h | 7 +- Modules/Core/include/mitkGenericLookupTable.h | 18 +++++ Modules/Core/include/mitkGenericProperty.h | 10 +++ .../Core/include/mitkLookupTableProperty.h | 3 + Modules/Core/include/mitkStringProperty.h | 3 + .../src/DataManagement/mitkColorProperty.cpp | 10 +++ .../mitkEnumerationProperty.cpp | 15 ++++ .../mitkLookupTableProperty.cpp | 76 +++++++++++++++++++ .../src/DataManagement/mitkStringProperty.cpp | 10 +++ 11 files changed, 174 insertions(+), 4 deletions(-) diff --git a/Modules/Core/include/mitkBaseProperty.h b/Modules/Core/include/mitkBaseProperty.h index 05a305dc505..a0f02b6da69 100644 --- a/Modules/Core/include/mitkBaseProperty.h +++ b/Modules/Core/include/mitkBaseProperty.h @@ -17,6 +17,7 @@ found in the LICENSE file. #include #include #include +#include namespace mitk { @@ -63,6 +64,9 @@ namespace mitk virtual std::string GetValueAsString() const; + virtual void ToJSON(nlohmann::json& j) const = 0; + virtual void FromJSON(const nlohmann::json& j) = 0; + /** * @brief Default return value if a property which can not be returned as string */ diff --git a/Modules/Core/include/mitkColorProperty.h b/Modules/Core/include/mitkColorProperty.h index 0b1809c0fba..a90567c2b1c 100644 --- a/Modules/Core/include/mitkColorProperty.h +++ b/Modules/Core/include/mitkColorProperty.h @@ -75,6 +75,9 @@ namespace mitk void SetValue(const mitk::Color &color); void SetColor(float red, float green, float blue); + void ToJSON(nlohmann::json &j) const override; + void FromJSON(const nlohmann::json &j) override; + using BaseProperty::operator=; private: @@ -93,4 +96,23 @@ namespace mitk } // namespace mitk +namespace itk +{ + template + void to_json(nlohmann::json& j, const RGBPixel& c) + { + j = nlohmann::json::array(); + + for (size_t i = 0; i < 3; ++i) + j.push_back(c[i]); + } + + template + void from_json(const nlohmann::json& j, RGBPixel& c) + { + for (size_t i = 0; i < 3; ++i) + j.at(i).get_to(c[i]); + } +} // namespace itk + #endif diff --git a/Modules/Core/include/mitkEnumerationProperty.h b/Modules/Core/include/mitkEnumerationProperty.h index 251f1cce820..3d5b8275429 100644 --- a/Modules/Core/include/mitkEnumerationProperty.h +++ b/Modules/Core/include/mitkEnumerationProperty.h @@ -164,6 +164,9 @@ namespace mitk EnumIdsContainerType &GetEnumIds(); EnumStringsContainerType &GetEnumStrings(); + void ToJSON(nlohmann::json& j) const override; + void FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; EnumerationProperty & operator=(const EnumerationProperty &) = delete; @@ -182,10 +185,6 @@ namespace mitk private: IdType m_CurrentValue; - - typedef std::map IdMapForClassNameContainerType; - typedef std::map StringMapForClassNameContainerType; - EnumIdsContainerType m_IdMap; EnumStringsContainerType m_NameMap; }; diff --git a/Modules/Core/include/mitkGenericLookupTable.h b/Modules/Core/include/mitkGenericLookupTable.h index c0ad940072a..43131a32ccd 100644 --- a/Modules/Core/include/mitkGenericLookupTable.h +++ b/Modules/Core/include/mitkGenericLookupTable.h @@ -23,6 +23,8 @@ found in the LICENSE file. #include "mitkNumericTypes.h" #include +#include + namespace mitk { /** @@ -83,9 +85,25 @@ namespace mitk } } + template + friend void from_json(const nlohmann::json&, GenericLookupTable&); + protected: LookupTableType m_LookupTable; }; + + template + void to_json(nlohmann::json& j, const GenericLookupTable& t) + { + j = t.GetLookupTable(); + } + + template + void from_json(const nlohmann::json& j, GenericLookupTable& t) + { + j.get_to(t.m_LookupTable); + } + } // namespace mitk /** diff --git a/Modules/Core/include/mitkGenericProperty.h b/Modules/Core/include/mitkGenericProperty.h index ceff2ad5e28..dad7c897da4 100644 --- a/Modules/Core/include/mitkGenericProperty.h +++ b/Modules/Core/include/mitkGenericProperty.h @@ -61,6 +61,16 @@ namespace mitk return myStr.str(); } + void ToJSON(nlohmann::json& j) const override + { + j = this->GetValue(); + } + + void FromJSON(const nlohmann::json& j) override + { + this->SetValue(j.get()); + } + using BaseProperty::operator=; protected: diff --git a/Modules/Core/include/mitkLookupTableProperty.h b/Modules/Core/include/mitkLookupTableProperty.h index 8a57cc0e835..f753b288b3d 100644 --- a/Modules/Core/include/mitkLookupTableProperty.h +++ b/Modules/Core/include/mitkLookupTableProperty.h @@ -64,6 +64,9 @@ namespace mitk std::string GetValueAsString() const override; + void ToJSON(nlohmann::json& j) const override; + void FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: diff --git a/Modules/Core/include/mitkStringProperty.h b/Modules/Core/include/mitkStringProperty.h index 04e7d744af3..574e426d705 100644 --- a/Modules/Core/include/mitkStringProperty.h +++ b/Modules/Core/include/mitkStringProperty.h @@ -55,6 +55,9 @@ namespace mitk std::string GetValueAsString() const override; + void ToJSON(nlohmann::json& j) const override; + void FromJSON(const nlohmann::json& j) override; + static const char *PATH; using BaseProperty::operator=; diff --git a/Modules/Core/src/DataManagement/mitkColorProperty.cpp b/Modules/Core/src/DataManagement/mitkColorProperty.cpp index 9288a7343e9..2340d23b94f 100644 --- a/Modules/Core/src/DataManagement/mitkColorProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkColorProperty.cpp @@ -82,6 +82,16 @@ const mitk::Color &mitk::ColorProperty::GetValue() const return GetColor(); } +void mitk::ColorProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetColor(); +} + +void mitk::ColorProperty::FromJSON(const nlohmann::json& j) +{ + this->SetColor(j.get()); +} + itk::LightObject::Pointer mitk::ColorProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); diff --git a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp index e6e3d278521..a8984514218 100644 --- a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp @@ -164,3 +164,18 @@ const mitk::EnumerationProperty::EnumStringsContainerType & mitk::EnumerationPro { return m_NameMap; } + +void mitk::EnumerationProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetValueAsString(); +} + +void mitk::EnumerationProperty::FromJSON(const nlohmann::json& j) +{ + auto name = j.get(); + + if (!this->IsValidEnumerationValue(name)) + mitkThrow() << '"' << name << "\" is not a valid enumeration value for " << this->GetNameOfClass() << '!'; + + this->SetValue(name); +} diff --git a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp index c079e4f0c41..d9edff7c6ba 100644 --- a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp @@ -65,6 +65,82 @@ void mitk::LookupTableProperty::SetValue(const ValueType &value) SetLookupTable(value); } +void mitk::LookupTableProperty::ToJSON(nlohmann::json& j) const +{ + auto lut = this->GetValue()->GetVtkLookupTable(); + + auto table = nlohmann::json::array(); + auto numTableValues = lut->GetNumberOfTableValues(); + + for (decltype(numTableValues) i = 0; i < numTableValues; ++i) + { + auto value = nlohmann::json::array(); + + for (size_t j = 0; j < 4; ++j) + value.push_back(lut->GetTableValue(i)[j]); + + table.push_back(value); + } + + j = nlohmann::json::object({ + { "NumberOfColors", static_cast(lut->GetNumberOfTableValues()) }, + { "Scale", lut->GetScale() }, + { "Ramp", lut->GetRamp() }, + { "HueRange", nlohmann::json::array({ lut->GetHueRange()[0], lut->GetHueRange()[1] }) }, + { "ValueRange", nlohmann::json::array({ lut->GetValueRange()[0], lut->GetValueRange()[1] }) }, + { "SaturationRange", nlohmann::json::array({ lut->GetSaturationRange()[0], lut->GetSaturationRange()[1] }) }, + { "AlphaRange", nlohmann::json::array({ lut->GetAlphaRange()[0], lut->GetAlphaRange()[1] }) }, + { "TableRange", nlohmann::json::array({ lut->GetTableRange()[0], lut->GetTableRange()[1] }) }, + { "Table", table } + }); +} + +void mitk::LookupTableProperty::FromJSON(const nlohmann::json& j) +{ + auto lut = vtkSmartPointer::New(); + + lut->SetNumberOfTableValues(j["NumberOfColors"].get()); + lut->SetScale(j["Scale"].get()); + lut->SetScale(j["Ramp"].get()); + + std::array range; + + j["HueRange"][0].get_to(range[0]); + j["HueRange"][1].get_to(range[1]); + lut->SetHueRange(range.data()); + + j["ValueRange"][0].get_to(range[0]); + j["ValueRange"][1].get_to(range[1]); + lut->SetValueRange(range.data()); + + j["SaturationRange"][0].get_to(range[0]); + j["SaturationRange"][1].get_to(range[1]); + lut->SetSaturationRange(range.data()); + + j["AlphaRange"][0].get_to(range[0]); + j["AlphaRange"][1].get_to(range[1]); + lut->SetAlphaRange(range.data()); + + j["TableRange"][0].get_to(range[0]); + j["TableRange"][1].get_to(range[1]); + lut->SetTableRange(range.data()); + + std::array rgba; + vtkIdType i = 0; + + for (const auto& value : j["Table"]) + { + for (size_t j = 0; j < 4; ++j) + value[j].get_to(rgba[j]); + + lut->SetTableValue(i++, rgba.data()); + } + + auto mitkLut = LookupTable::New(); + mitkLut->SetVtkLookupTable(lut); + this->SetLookupTable(mitkLut); +} + itk::LightObject::Pointer mitk::LookupTableProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); diff --git a/Modules/Core/src/DataManagement/mitkStringProperty.cpp b/Modules/Core/src/DataManagement/mitkStringProperty.cpp index 767d6101c11..88b088ef259 100644 --- a/Modules/Core/src/DataManagement/mitkStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkStringProperty.cpp @@ -43,6 +43,16 @@ std::string mitk::StringProperty::GetValueAsString() const return m_Value; } +void mitk::StringProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetValueAsString(); +} + +void mitk::StringProperty::FromJSON(const nlohmann::json& j) +{ + this->SetValue(j.get()); +} + itk::LightObject::Pointer mitk::StringProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); From f777dc1da48e546fb451ffe9cbaa33049eb1842e Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 18 Oct 2023 15:20:19 +0200 Subject: [PATCH 05/92] Complete core property JSON serialization --- Modules/Core/include/mitkAnnotationProperty.h | 4 + Modules/Core/include/mitkBaseProperty.h | 19 +- Modules/Core/include/mitkClippingProperty.h | 3 + Modules/Core/include/mitkColorProperty.h | 4 +- .../Core/include/mitkEnumerationProperty.h | 4 +- Modules/Core/include/mitkGenericProperty.h | 6 +- Modules/Core/include/mitkGroupTagProperty.h | 5 +- Modules/Core/include/mitkLevelWindow.h | 5 + .../Core/include/mitkLevelWindowProperty.h | 3 + .../Core/include/mitkLookupTableProperty.h | 4 +- Modules/Core/include/mitkPoint.h | 2 +- .../Core/include/mitkSmartPointerProperty.h | 3 + Modules/Core/include/mitkStringProperty.h | 4 +- .../mitkTemporoSpatialStringProperty.h | 5 +- .../include/mitkTransferFunctionProperty.h | 3 + Modules/Core/include/mitkVector.h | 2 +- Modules/Core/include/mitkVectorProperty.h | 3 + .../Core/include/mitkWeakPointerProperty.h | 3 + .../DataManagement/mitkAnnotationProperty.cpp | 16 + .../DataManagement/mitkClippingProperty.cpp | 19 ++ .../src/DataManagement/mitkColorProperty.cpp | 7 +- .../mitkEnumerationProperty.cpp | 10 +- .../DataManagement/mitkGroupTagProperty.cpp | 12 + .../src/DataManagement/mitkLevelWindow.cpp | 38 +++ .../mitkLevelWindowProperty.cpp | 12 + .../mitkLookupTableProperty.cpp | 13 +- .../mitkSmartPointerProperty.cpp | 10 + .../src/DataManagement/mitkStringProperty.cpp | 7 +- .../mitkTemporoSpatialStringProperty.cpp | 283 +++++++----------- .../mitkTransferFunctionProperty.cpp | 72 +++++ .../src/DataManagement/mitkVectorProperty.cpp | 15 + .../mitkWeakPointerProperty.cpp | 10 + .../mitkTemporoSpatialStringPropertyTest.cpp | 21 +- .../include/mitkScalarListLookupTable.h | 4 + .../src/Common/mitkScalarListLookupTable.cpp | 15 + .../RT/include/mitkIsoDoseLevelSetProperty.h | 3 + .../include/mitkIsoDoseLevelVectorProperty.h | 3 + .../RT/src/mitkIsoDoseLevelSetProperty.cpp | 10 + .../RT/src/mitkIsoDoseLevelVectorProperty.cpp | 10 + 39 files changed, 459 insertions(+), 213 deletions(-) diff --git a/Modules/Core/include/mitkAnnotationProperty.h b/Modules/Core/include/mitkAnnotationProperty.h index 310a4324051..9c03069090a 100644 --- a/Modules/Core/include/mitkAnnotationProperty.h +++ b/Modules/Core/include/mitkAnnotationProperty.h @@ -49,6 +49,10 @@ namespace mitk void SetPosition(const Point3D &position); std::string GetValueAsString() const override; + + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + virtual BaseProperty &operator=(const BaseProperty &other) { return Superclass::operator=(other); } using BaseProperty::operator=; diff --git a/Modules/Core/include/mitkBaseProperty.h b/Modules/Core/include/mitkBaseProperty.h index a0f02b6da69..f99c775f5d7 100644 --- a/Modules/Core/include/mitkBaseProperty.h +++ b/Modules/Core/include/mitkBaseProperty.h @@ -17,7 +17,7 @@ found in the LICENSE file. #include #include #include -#include +#include namespace mitk { @@ -64,8 +64,21 @@ namespace mitk virtual std::string GetValueAsString() const; - virtual void ToJSON(nlohmann::json& j) const = 0; - virtual void FromJSON(const nlohmann::json& j) = 0; + /** \brief Serialize property value(s) to JSON. + * + * Rely on exceptions for error handling when implementing serialization. + * + * \return False if not serializable by design, true otherwise. + */ + virtual bool ToJSON(nlohmann::json& j) const = 0; + + /** \brief Deserialize property value(s) from JSON. + * + * Rely on exceptions for error handling when implementing deserialization. + * + * \return False if not deserializable by design, true otherwise. + */ + virtual bool FromJSON(const nlohmann::json& j) = 0; /** * @brief Default return value if a property which can not be returned as string diff --git a/Modules/Core/include/mitkClippingProperty.h b/Modules/Core/include/mitkClippingProperty.h index 87bdc056b15..9e3f5325729 100644 --- a/Modules/Core/include/mitkClippingProperty.h +++ b/Modules/Core/include/mitkClippingProperty.h @@ -56,6 +56,9 @@ namespace mitk std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: diff --git a/Modules/Core/include/mitkColorProperty.h b/Modules/Core/include/mitkColorProperty.h index a90567c2b1c..c4a3ae0be47 100644 --- a/Modules/Core/include/mitkColorProperty.h +++ b/Modules/Core/include/mitkColorProperty.h @@ -75,8 +75,8 @@ namespace mitk void SetValue(const mitk::Color &color); void SetColor(float red, float green, float blue); - void ToJSON(nlohmann::json &j) const override; - void FromJSON(const nlohmann::json &j) override; + bool ToJSON(nlohmann::json &j) const override; + bool FromJSON(const nlohmann::json &j) override; using BaseProperty::operator=; diff --git a/Modules/Core/include/mitkEnumerationProperty.h b/Modules/Core/include/mitkEnumerationProperty.h index 3d5b8275429..8ec695d872c 100644 --- a/Modules/Core/include/mitkEnumerationProperty.h +++ b/Modules/Core/include/mitkEnumerationProperty.h @@ -164,8 +164,8 @@ namespace mitk EnumIdsContainerType &GetEnumIds(); EnumStringsContainerType &GetEnumStrings(); - void ToJSON(nlohmann::json& j) const override; - void FromJSON(const nlohmann::json& j) override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; using BaseProperty::operator=; EnumerationProperty & operator=(const EnumerationProperty &) = delete; diff --git a/Modules/Core/include/mitkGenericProperty.h b/Modules/Core/include/mitkGenericProperty.h index dad7c897da4..1f2cecc5614 100644 --- a/Modules/Core/include/mitkGenericProperty.h +++ b/Modules/Core/include/mitkGenericProperty.h @@ -61,14 +61,16 @@ namespace mitk return myStr.str(); } - void ToJSON(nlohmann::json& j) const override + bool ToJSON(nlohmann::json& j) const override { j = this->GetValue(); + return true; } - void FromJSON(const nlohmann::json& j) override + bool FromJSON(const nlohmann::json& j) override { this->SetValue(j.get()); + return true; } using BaseProperty::operator=; diff --git a/Modules/Core/include/mitkGroupTagProperty.h b/Modules/Core/include/mitkGroupTagProperty.h index e83ff9b0e5f..9d17596bc19 100644 --- a/Modules/Core/include/mitkGroupTagProperty.h +++ b/Modules/Core/include/mitkGroupTagProperty.h @@ -37,7 +37,10 @@ namespace mitk itkFactorylessNewMacro(Self); itkCloneMacro(Self); - using BaseProperty::operator=; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + + using BaseProperty::operator=; protected: GroupTagProperty(); diff --git a/Modules/Core/include/mitkLevelWindow.h b/Modules/Core/include/mitkLevelWindow.h index f81f33c2001..3b0c2006ad2 100644 --- a/Modules/Core/include/mitkLevelWindow.h +++ b/Modules/Core/include/mitkLevelWindow.h @@ -14,6 +14,7 @@ found in the LICENSE file. #include "mitkNumericTypes.h" #include +#include namespace mitk { @@ -259,5 +260,9 @@ namespace mitk */ inline void EnsureConsistency(); }; + + MITKCORE_EXPORT void to_json(nlohmann::json& j, const LevelWindow& lw); + MITKCORE_EXPORT void from_json(const nlohmann::json& j, LevelWindow& lw); + } // namespace mitk #endif diff --git a/Modules/Core/include/mitkLevelWindowProperty.h b/Modules/Core/include/mitkLevelWindowProperty.h index 4c4325de225..30209a6f266 100755 --- a/Modules/Core/include/mitkLevelWindowProperty.h +++ b/Modules/Core/include/mitkLevelWindowProperty.h @@ -64,6 +64,9 @@ namespace mitk std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: diff --git a/Modules/Core/include/mitkLookupTableProperty.h b/Modules/Core/include/mitkLookupTableProperty.h index f753b288b3d..3b728378582 100644 --- a/Modules/Core/include/mitkLookupTableProperty.h +++ b/Modules/Core/include/mitkLookupTableProperty.h @@ -64,8 +64,8 @@ namespace mitk std::string GetValueAsString() const override; - void ToJSON(nlohmann::json& j) const override; - void FromJSON(const nlohmann::json& j) override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; using BaseProperty::operator=; diff --git a/Modules/Core/include/mitkPoint.h b/Modules/Core/include/mitkPoint.h index f8844cbae80..0f9c36682b2 100644 --- a/Modules/Core/include/mitkPoint.h +++ b/Modules/Core/include/mitkPoint.h @@ -106,7 +106,7 @@ namespace mitk void from_json(const nlohmann::json& j, Point& p) { for (size_t i = 0; i < NPointDimension; ++i) - p[i] = j.at(i).get(); + j.at(i).get_to(p[i]); } typedef Point Point2D; diff --git a/Modules/Core/include/mitkSmartPointerProperty.h b/Modules/Core/include/mitkSmartPointerProperty.h index aac5f37d697..ddf84f66b38 100644 --- a/Modules/Core/include/mitkSmartPointerProperty.h +++ b/Modules/Core/include/mitkSmartPointerProperty.h @@ -57,6 +57,9 @@ namespace mitk static std::string GetReferenceUIDFor(itk::Object *); static void RegisterPointerTarget(itk::Object *, const std::string uid); + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: diff --git a/Modules/Core/include/mitkStringProperty.h b/Modules/Core/include/mitkStringProperty.h index 574e426d705..41962df4e19 100644 --- a/Modules/Core/include/mitkStringProperty.h +++ b/Modules/Core/include/mitkStringProperty.h @@ -55,8 +55,8 @@ namespace mitk std::string GetValueAsString() const override; - void ToJSON(nlohmann::json& j) const override; - void FromJSON(const nlohmann::json& j) override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; static const char *PATH; diff --git a/Modules/Core/include/mitkTemporoSpatialStringProperty.h b/Modules/Core/include/mitkTemporoSpatialStringProperty.h index 595c8d1acaf..8f6f090f937 100644 --- a/Modules/Core/include/mitkTemporoSpatialStringProperty.h +++ b/Modules/Core/include/mitkTemporoSpatialStringProperty.h @@ -87,6 +87,9 @@ namespace mitk information to retrieve the stored value.*/ bool IsUniform() const; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: @@ -118,7 +121,7 @@ namespace mitk namespace PropertyPersistenceSerialization { /** Serialization of a TemporoSpatialStringProperty into a JSON string.*/ - MITKCORE_EXPORT::std::string serializeTemporoSpatialStringPropertyToJSON(const mitk::BaseProperty *prop); + MITKCORE_EXPORT std::string serializeTemporoSpatialStringPropertyToJSON(const mitk::BaseProperty *prop); } namespace PropertyPersistenceDeserialization diff --git a/Modules/Core/include/mitkTransferFunctionProperty.h b/Modules/Core/include/mitkTransferFunctionProperty.h index e8bf9439ccb..fd3c20d7368 100644 --- a/Modules/Core/include/mitkTransferFunctionProperty.h +++ b/Modules/Core/include/mitkTransferFunctionProperty.h @@ -50,6 +50,9 @@ namespace mitk std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: diff --git a/Modules/Core/include/mitkVector.h b/Modules/Core/include/mitkVector.h index ac111e18ee2..6c5bc2ddaf9 100644 --- a/Modules/Core/include/mitkVector.h +++ b/Modules/Core/include/mitkVector.h @@ -146,7 +146,7 @@ namespace mitk void from_json(const nlohmann::json &j, Vector &v) { for (size_t i = 0; i < NVectorDimension; ++i) - v[i] = j.at(i).get(); + j.at(i).get_to(v[i]); } // convenience typedefs for often used mitk::Vector representations. diff --git a/Modules/Core/include/mitkVectorProperty.h b/Modules/Core/include/mitkVectorProperty.h index be0b00f7679..547a5d5caf8 100644 --- a/Modules/Core/include/mitkVectorProperty.h +++ b/Modules/Core/include/mitkVectorProperty.h @@ -93,6 +93,9 @@ namespace mitk /// sets the content vector virtual void SetValue(const VectorType ¶meter_vector); + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + private: /// purposely not implemented VectorProperty &operator=(const Self &); diff --git a/Modules/Core/include/mitkWeakPointerProperty.h b/Modules/Core/include/mitkWeakPointerProperty.h index aba93c9c375..db11e06a194 100644 --- a/Modules/Core/include/mitkWeakPointerProperty.h +++ b/Modules/Core/include/mitkWeakPointerProperty.h @@ -50,6 +50,9 @@ namespace mitk std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: diff --git a/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp b/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp index 303e0c80f0b..d3463856e35 100644 --- a/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp @@ -96,3 +96,19 @@ itk::LightObject::Pointer mitk::AnnotationProperty::InternalClone() const result->UnRegister(); return result; } + +bool mitk::AnnotationProperty::ToJSON(nlohmann::json& j) const +{ + j = nlohmann::json{ + {"Label", this->GetLabel()}, + {"Position", this->GetPosition()}}; + + return true; +} + +bool mitk::AnnotationProperty::FromJSON(const nlohmann::json& j) +{ + this->SetLabel(j["Label"].get()); + this->SetPosition(j["Position"].get()); + return true; +} diff --git a/Modules/Core/src/DataManagement/mitkClippingProperty.cpp b/Modules/Core/src/DataManagement/mitkClippingProperty.cpp index 5dc7014d1e9..b3e5a677e4d 100644 --- a/Modules/Core/src/DataManagement/mitkClippingProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkClippingProperty.cpp @@ -81,6 +81,25 @@ namespace mitk return myStr.str(); } + bool ClippingProperty::ToJSON(nlohmann::json& j) const + { + j = nlohmann::json{ + {"Enabled", this->GetClippingEnabled()}, + {"Origin", this->GetOrigin()}, + {"Normal", this->GetNormal()}}; + + return true; + } + + bool ClippingProperty::FromJSON(const nlohmann::json& j) + { + this->SetClippingEnabled(j["Enabled"].get()); + this->SetOrigin(j["Origin"].get()); + this->SetNormal(j["Normal"].get()); + + return true; + } + itk::LightObject::Pointer ClippingProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); diff --git a/Modules/Core/src/DataManagement/mitkColorProperty.cpp b/Modules/Core/src/DataManagement/mitkColorProperty.cpp index 2340d23b94f..25d48f86a3b 100644 --- a/Modules/Core/src/DataManagement/mitkColorProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkColorProperty.cpp @@ -12,6 +12,7 @@ found in the LICENSE file. #include "mitkColorProperty.h" #include +#include mitk::ColorProperty::ColorProperty() : m_Color(0.0f) { @@ -82,14 +83,16 @@ const mitk::Color &mitk::ColorProperty::GetValue() const return GetColor(); } -void mitk::ColorProperty::ToJSON(nlohmann::json& j) const +bool mitk::ColorProperty::ToJSON(nlohmann::json& j) const { j = this->GetColor(); + return true; } -void mitk::ColorProperty::FromJSON(const nlohmann::json& j) +bool mitk::ColorProperty::FromJSON(const nlohmann::json& j) { this->SetColor(j.get()); + return true; } itk::LightObject::Pointer mitk::ColorProperty::InternalClone() const diff --git a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp index a8984514218..2019bf53754 100644 --- a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp @@ -12,6 +12,7 @@ found in the LICENSE file. #include #include +#include mitk::EnumerationProperty::EnumerationProperty() : m_CurrentValue(0) @@ -165,17 +166,18 @@ const mitk::EnumerationProperty::EnumStringsContainerType & mitk::EnumerationPro return m_NameMap; } -void mitk::EnumerationProperty::ToJSON(nlohmann::json& j) const +bool mitk::EnumerationProperty::ToJSON(nlohmann::json& j) const { j = this->GetValueAsString(); + return true; } -void mitk::EnumerationProperty::FromJSON(const nlohmann::json& j) +bool mitk::EnumerationProperty::FromJSON(const nlohmann::json& j) { auto name = j.get(); - if (!this->IsValidEnumerationValue(name)) + if (!this->SetValue(name)) mitkThrow() << '"' << name << "\" is not a valid enumeration value for " << this->GetNameOfClass() << '!'; - this->SetValue(name); + return true; } diff --git a/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp b/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp index 2fc2aa5e052..272bbec8ff9 100644 --- a/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp @@ -11,6 +11,7 @@ found in the LICENSE file. ============================================================================*/ #include "mitkGroupTagProperty.h" +#include mitk::GroupTagProperty::GroupTagProperty() : mitk::BaseProperty() { @@ -31,6 +32,17 @@ bool mitk::GroupTagProperty::Assign(const BaseProperty & /*property*/) return true; } +bool mitk::GroupTagProperty::ToJSON(nlohmann::json& j) const +{ + j = nlohmann::json::object(); + return true; +} + +bool mitk::GroupTagProperty::FromJSON(const nlohmann::json&) +{ + return true; +} + itk::LightObject::Pointer mitk::GroupTagProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp index c3d88419192..be8a030d6ac 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp @@ -16,6 +16,7 @@ found in the LICENSE file. #include "mitkImageStatisticsHolder.h" #include +#include void mitk::LevelWindow::EnsureConsistency() { @@ -508,3 +509,40 @@ mitk::LevelWindow &mitk::LevelWindow::operator=(const mitk::LevelWindow &levWin) return *this; } } + +namespace mitk +{ + void to_json(nlohmann::json& j, const LevelWindow& lw) + { + j = nlohmann::json{ + {"Fixed", lw.IsFixed()}, + {"IsFloatingImage", lw.IsFloatingValues()}, + {"CurrentSettings", { + {"Level", lw.GetLevel()}, + {"Window", lw.GetWindow()}}}, + {"DefaultSettings", { + {"Level", lw.GetDefaultLevel()}, + {"Window", lw.GetDefaultWindow()}}}, + {"CurrentRange", { + {"Min", lw.GetRangeMin()}, + {"Max", lw.GetRangeMax()}}}}; + } + + void from_json(const nlohmann::json& j, LevelWindow& lw) + { + lw.SetRangeMinMax( + j["CurrentRange"]["Min"].get(), + j["CurrentRange"]["Max"].get()); + + lw.SetDefaultLevelWindow( + j["DefaultSettings"]["Level"].get(), + j["DefaultSettings"]["Window"].get()); + + lw.SetLevelWindow( + j["CurrentSettings"]["Level"].get(), + j["CurrentSettings"]["Window"].get()); + + lw.SetFixed(j["Fixed"].get()); + lw.SetFloatingValues(j["IsFloatingImage"].get()); + } +} diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp index 5376d63183d..2f82af0ba77 100755 --- a/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp @@ -72,6 +72,18 @@ std::string mitk::LevelWindowProperty::GetValueAsString() const return myStr.str(); } +bool mitk::LevelWindowProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetLevelWindow(); + return true; +} + +bool mitk::LevelWindowProperty::FromJSON(const nlohmann::json& j) +{ + this->SetLevelWindow(j.get()); + return true; +} + itk::LightObject::Pointer mitk::LevelWindowProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); diff --git a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp index d9edff7c6ba..63751f8bfa1 100644 --- a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp @@ -11,6 +11,7 @@ found in the LICENSE file. ============================================================================*/ #include "mitkLookupTableProperty.h" +#include mitk::LookupTableProperty::LookupTableProperty() { @@ -65,7 +66,7 @@ void mitk::LookupTableProperty::SetValue(const ValueType &value) SetLookupTable(value); } -void mitk::LookupTableProperty::ToJSON(nlohmann::json& j) const +bool mitk::LookupTableProperty::ToJSON(nlohmann::json& j) const { auto lut = this->GetValue()->GetVtkLookupTable(); @@ -82,7 +83,7 @@ void mitk::LookupTableProperty::ToJSON(nlohmann::json& j) const table.push_back(value); } - j = nlohmann::json::object({ + j = nlohmann::json{{ { "NumberOfColors", static_cast(lut->GetNumberOfTableValues()) }, { "Scale", lut->GetScale() }, { "Ramp", lut->GetRamp() }, @@ -92,10 +93,12 @@ void mitk::LookupTableProperty::ToJSON(nlohmann::json& j) const { "AlphaRange", nlohmann::json::array({ lut->GetAlphaRange()[0], lut->GetAlphaRange()[1] }) }, { "TableRange", nlohmann::json::array({ lut->GetTableRange()[0], lut->GetTableRange()[1] }) }, { "Table", table } - }); + }}; + + return true; } -void mitk::LookupTableProperty::FromJSON(const nlohmann::json& j) +bool mitk::LookupTableProperty::FromJSON(const nlohmann::json& j) { auto lut = vtkSmartPointer::New(); @@ -139,6 +142,8 @@ void mitk::LookupTableProperty::FromJSON(const nlohmann::json& j) auto mitkLut = LookupTable::New(); mitkLut->SetVtkLookupTable(lut); this->SetLookupTable(mitkLut); + + return true; } itk::LightObject::Pointer mitk::LookupTableProperty::InternalClone() const diff --git a/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp b/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp index 0ca671f20d8..dad9fa882d8 100644 --- a/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp @@ -128,6 +128,16 @@ std::string mitk::SmartPointerProperty::GetValueAsString() const return std::string("nullptr"); } +bool mitk::SmartPointerProperty::ToJSON(nlohmann::json&) const +{ + return false; +} + +bool mitk::SmartPointerProperty::FromJSON(const nlohmann::json&) +{ + return false; +} + itk::LightObject::Pointer mitk::SmartPointerProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); diff --git a/Modules/Core/src/DataManagement/mitkStringProperty.cpp b/Modules/Core/src/DataManagement/mitkStringProperty.cpp index 88b088ef259..cc1ecf9e697 100644 --- a/Modules/Core/src/DataManagement/mitkStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkStringProperty.cpp @@ -11,6 +11,7 @@ found in the LICENSE file. ============================================================================*/ #include "mitkStringProperty.h" +#include const char *mitk::StringProperty::PATH = "path"; mitk::StringProperty::StringProperty(const char *string) : m_Value() @@ -43,14 +44,16 @@ std::string mitk::StringProperty::GetValueAsString() const return m_Value; } -void mitk::StringProperty::ToJSON(nlohmann::json& j) const +bool mitk::StringProperty::ToJSON(nlohmann::json& j) const { j = this->GetValueAsString(); + return true; } -void mitk::StringProperty::FromJSON(const nlohmann::json& j) +bool mitk::StringProperty::FromJSON(const nlohmann::json& j) { this->SetValue(j.get()); + return true; } itk::LightObject::Pointer mitk::StringProperty::InternalClone() const diff --git a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp index a3e0b1f549e..6a2eeb4a091 100644 --- a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp @@ -18,7 +18,59 @@ found in the LICENSE file. #include -using namespace nlohmann; +using CondensedTimeKeyType = std::pair; +using CondensedTimePointsType = std::map; + +using CondensedSliceKeyType = std::pair; +using CondensedSlicesType = std::map; + +namespace +{ + /** Helper function that checks if between an ID and a successive ID is no gap.*/ + template + bool isGap(const TValue& value, const TValue& successor) + { + return value successor + 1; + } + + + template + void CheckAndCondenseElement(const TNewKey& newKeyMinID, const TNewValue& newValue, TMasterKey& masterKey, TMasterValue& masterValue, TCondensedContainer& condensedContainer) + { + if (newValue != masterValue + || isGap(newKeyMinID, masterKey.second)) + { + condensedContainer[masterKey] = masterValue; + masterValue = newValue; + masterKey.first = newKeyMinID; + } + masterKey.second = newKeyMinID; + } + + /** Helper function that tries to condense the values of time points for a slice as much as possible and returns all slices with condensed timepoint values.*/ + CondensedSlicesType CondenseTimePointValuesOfProperty(const mitk::TemporoSpatialStringProperty* tsProp) + { + CondensedSlicesType uncondensedSlices; + + auto zs = tsProp->GetAvailableSlices(); + for (const auto z : zs) + { + CondensedTimePointsType condensedTimePoints; + auto timePointIDs = tsProp->GetAvailableTimeSteps(z); + CondensedTimeKeyType condensedKey = { timePointIDs.front(),timePointIDs.front() }; + auto refValue = tsProp->GetValue(timePointIDs.front(), z); + + for (const auto timePointID : timePointIDs) + { + const auto& newVal = tsProp->GetValue(timePointID, z); + CheckAndCondenseElement(timePointID, newVal, condensedKey, refValue, condensedTimePoints); + } + condensedTimePoints[condensedKey] = refValue; + uncondensedSlices[{ z, z }] = condensedTimePoints; + } + return uncondensedSlices; + } +} mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const char *s) { @@ -267,212 +319,67 @@ void mitk::TemporoSpatialStringProperty::SetValue(const ValueType &value) this->SetValue(0, 0, value); }; -// Create necessary escape sequences from illegal characters -// REMARK: This code is based upon code from boost::ptree::json_writer. -// The corresponding boost function was not used directly, because it is not part of -// the public interface of ptree::json_writer. :( -// A own serialization strategy was implemented instead of using boost::ptree::json_write because -// currently (<= boost 1.60) everything (even numbers) are converted into string representations -// by the writer, so e.g. it becomes "t":"2" instead of "t":2 -template -std::basic_string CreateJSONEscapes(const std::basic_string &s) +bool mitk::TemporoSpatialStringProperty::ToJSON(nlohmann::json& j) const { - std::basic_string result; - typename std::basic_string::const_iterator b = s.begin(); - typename std::basic_string::const_iterator e = s.end(); - while (b != e) - { - using UCh = std::make_unsigned_t; - UCh c(*b); - // This assumes an ASCII superset. - // We escape everything outside ASCII, because this code can't - // handle high unicode characters. - if (c == 0x20 || c == 0x21 || (c >= 0x23 && c <= 0x2E) || (c >= 0x30 && c <= 0x5B) || (c >= 0x5D && c <= 0x7F)) - result += *b; - else if (*b == Ch('\b')) - result += Ch('\\'), result += Ch('b'); - else if (*b == Ch('\f')) - result += Ch('\\'), result += Ch('f'); - else if (*b == Ch('\n')) - result += Ch('\\'), result += Ch('n'); - else if (*b == Ch('\r')) - result += Ch('\\'), result += Ch('r'); - else if (*b == Ch('\t')) - result += Ch('\\'), result += Ch('t'); - else if (*b == Ch('/')) - result += Ch('\\'), result += Ch('/'); - else if (*b == Ch('"')) - result += Ch('\\'), result += Ch('"'); - else if (*b == Ch('\\')) - result += Ch('\\'), result += Ch('\\'); - else - { - const char *hexdigits = "0123456789ABCDEF"; - unsigned long u = (std::min)(static_cast(static_cast(*b)), 0xFFFFul); - int d1 = u / 4096; - u -= d1 * 4096; - int d2 = u / 256; - u -= d2 * 256; - int d3 = u / 16; - u -= d3 * 16; - int d4 = u; - result += Ch('\\'); - result += Ch('u'); - result += Ch(hexdigits[d1]); - result += Ch(hexdigits[d2]); - result += Ch(hexdigits[d3]); - result += Ch(hexdigits[d4]); - } - ++b; - } - return result; -} - -using CondensedTimeKeyType = std::pair; -using CondensedTimePointsType = std::map; - -using CondensedSliceKeyType = std::pair; -using CondensedSlicesType = std::map; - -/** Helper function that checks if between an ID and a successive ID is no gap.*/ -template -bool isGap(const TValue& value, const TValue& successor) -{ - return value successor + 1; -} - - -template -void CheckAndCondenseElement(const TNewKey& newKeyMinID, const TNewValue& newValue, TMasterKey& masterKey, TMasterValue& masterValue, TCondensedContainer& condensedContainer) -{ - if (newValue != masterValue - || isGap(newKeyMinID, masterKey.second)) - { - condensedContainer[masterKey] = masterValue; - masterValue = newValue; - masterKey.first = newKeyMinID; - } - masterKey.second = newKeyMinID; -} - -/** Helper function that tries to condense the values of time points for a slice as much as possible and returns all slices with condensed timepoint values.*/ -CondensedSlicesType CondenseTimePointValuesOfProperty(const mitk::TemporoSpatialStringProperty* tsProp) -{ - CondensedSlicesType uncondensedSlices; - - auto zs = tsProp->GetAvailableSlices(); - for (const auto z : zs) - { - CondensedTimePointsType condensedTimePoints; - auto timePointIDs = tsProp->GetAvailableTimeSteps(z); - CondensedTimeKeyType condensedKey = { timePointIDs.front(),timePointIDs.front() }; - auto refValue = tsProp->GetValue(timePointIDs.front(), z); - - for (const auto timePointID : timePointIDs) - { - const auto& newVal = tsProp->GetValue(timePointID, z); - CheckAndCondenseElement(timePointID, newVal, condensedKey, refValue, condensedTimePoints); - } - condensedTimePoints[condensedKey] = refValue; - uncondensedSlices[{ z, z }] = condensedTimePoints; - } - return uncondensedSlices; -} - -::std::string mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON( - const mitk::BaseProperty *prop) -{ - // REMARK: Implemented own serialization instead of using boost::ptree::json_write because - // currently (<= boost 1.60) everything (even numbers) are converted into string representations - // by the writer, so e.g. it becomes "t":"2" instead of "t":2 - // If this problem is fixed with boost, we should switch back to json_writer (and remove the custom - // implementation of CreateJSONEscapes (see above)). - const auto *tsProp = dynamic_cast(prop); - - if (!tsProp) - { - mitkThrow() << "Cannot serialize properties of types other than TemporoSpatialStringProperty."; - } - - std::ostringstream stream; - stream.imbue(std::locale("C")); - stream << "{\"values\":["; - - //we condense the content of the property to have a compact serialization. - //we start with condensing time points and then slices (in difference to the - //internal layout). Reason: There is more entropy in slices (looking at DICOM) - //than across time points for one slice, so we can "compress" to a higher rate. - //We don't wanted to change the internal structure of the property as it would - //introduce API inconvenience and subtle changes in behavior. - CondensedSlicesType uncondensedSlices = CondenseTimePointValuesOfProperty(tsProp); - - //now condense the slices + // We condense the content of the property to have a compact serialization. + // We start with condensing time points and then slices (in difference to the + // internal layout). Reason: There is more entropy in slices (looking at DICOM) + // than across time points for one slice, so we can "compress" at a higher rate. + // We didn't want to change the internal structure of the property as it would + // introduce API inconvenience and subtle changes in behavior. + auto uncondensedSlices = CondenseTimePointValuesOfProperty(this); CondensedSlicesType condensedSlices; + if(!uncondensedSlices.empty()) { - CondensedTimePointsType& masterSlice = uncondensedSlices.begin()->second; - CondensedSliceKeyType masterSliceKey = uncondensedSlices.begin()->first; + auto& masterSlice = uncondensedSlices.begin()->second; + auto masterSliceKey = uncondensedSlices.begin()->first; for (const auto& uncondensedSlice : uncondensedSlices) { const auto& uncondensedSliceID = uncondensedSlice.first.first; CheckAndCondenseElement(uncondensedSliceID, uncondensedSlice.second, masterSliceKey, masterSlice, condensedSlices); } + condensedSlices[masterSliceKey] = masterSlice; } + auto values = nlohmann::json::array(); - bool first = true; for (const auto& z : condensedSlices) { for (const auto& t : z.second) { - if (first) - { - first = false; - } - else - { - stream << ", "; - } - const auto& minSliceID = z.first.first; const auto& maxSliceID = z.first.second; const auto& minTimePointID = t.first.first; const auto& maxTimePointID = t.first.second; - stream << "{\"z\":" << minSliceID << ", "; + auto value = nlohmann::json::object(); + value.push_back({"z", minSliceID}); + if (minSliceID != maxSliceID) - { - stream << "\"zmax\":" << maxSliceID << ", "; - } - stream << "\"t\":" << minTimePointID << ", "; + value.push_back({"zmax", maxSliceID}); + + value.push_back({"t", minTimePointID}); + if (minTimePointID != maxTimePointID) - { - stream << "\"tmax\":" << maxTimePointID << ", "; - } + value.push_back({"tmax", maxTimePointID}); + + value.push_back({"value", t.second}); - const auto& value = t.second; - stream << "\"value\":\"" << CreateJSONEscapes(value) << "\"}"; + values.push_back(value); } } - stream << "]}"; + j = nlohmann::json{{"values", values}}; - return stream.str(); + return true; } -mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty( - const std::string &value) +bool mitk::TemporoSpatialStringProperty::FromJSON(const nlohmann::json& j) { - if (value.empty()) - return nullptr; - - mitk::TemporoSpatialStringProperty::Pointer prop = mitk::TemporoSpatialStringProperty::New(); - - auto root = json::parse(value); - - for (const auto& element : root["values"]) + for (const auto& element : j["values"]) { auto value = element.value("value", ""); auto z = element.value("z", 0); @@ -484,10 +391,36 @@ mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializ { for (auto currentZ = z; currentZ <= zmax; ++currentZ) { - prop->SetValue(currentT, currentZ, value); + this->SetValue(currentT, currentZ, value); } } } + return true; +} + +std::string mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(const mitk::BaseProperty *prop) +{ + const auto *tsProp = dynamic_cast(prop); + + if (!tsProp) + mitkThrow() << "Cannot serialize properties of types other than TemporoSpatialStringProperty."; + + nlohmann::json j; + tsProp->ToJSON(j); + + return j.dump(); +} + +mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(const std::string &value) +{ + if (value.empty()) + return nullptr; + + auto prop = mitk::TemporoSpatialStringProperty::New(); + + auto root = nlohmann::json::parse(value); + prop->FromJSON(root); + return prop.GetPointer(); } diff --git a/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp b/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp index 3e5bce2ca67..20a59af2a76 100644 --- a/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp @@ -11,6 +11,7 @@ found in the LICENSE file. ============================================================================*/ #include "mitkTransferFunctionProperty.h" +#include namespace mitk { @@ -43,6 +44,77 @@ namespace mitk { } + bool TransferFunctionProperty::ToJSON(nlohmann::json& j) const + { + auto tf = this->GetValue(); + + auto scalarOpacity = nlohmann::json::array(); + + for (const auto& point : tf->GetScalarOpacityPoints()) + scalarOpacity.push_back(point); + + auto gradientOpacity = nlohmann::json::array(); + + for (const auto& point : tf->GetGradientOpacityPoints()) + gradientOpacity.push_back(point); + + auto* ctf = tf->GetColorTransferFunction(); + auto size = ctf->GetSize(); + + std::array value; + auto color = nlohmann::json::array(); + + for (int i = 0; i < size; ++i) + { + ctf->GetNodeValue(i, value.data()); + color.push_back(value); + } + + j = nlohmann::json{ + {"ScalarOpacity", scalarOpacity}, + {"GradientOpacity", gradientOpacity}, + {"Color", color}}; + + return true; + } + + bool TransferFunctionProperty::FromJSON(const nlohmann::json& j) + { + auto tf = TransferFunction::New(); + TransferFunction::ControlPoints::value_type point; + + tf->ClearScalarOpacityPoints(); + + for (const auto& opacity : j["ScalarOpacity"]) + { + opacity.get_to(point); + tf->AddScalarOpacityPoint(point.first, point.second); + } + + tf->ClearGradientOpacityPoints(); + + for (const auto& opacity : j["GradientOpacity"]) + { + opacity.get_to(point); + tf->AddGradientOpacityPoint(point.first, point.second); + } + + auto* ctf = tf->GetColorTransferFunction(); + ctf->RemoveAllPoints(); + + std::array value; + + for (const auto& color : j["Color"]) + { + color.get_to(value); + ctf->AddRGBPoint(value[0], value[1], value[2], value[3], value[4], value[5]); + } + + this->SetValue(tf); + + return true; + } + itk::LightObject::Pointer TransferFunctionProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); diff --git a/Modules/Core/src/DataManagement/mitkVectorProperty.cpp b/Modules/Core/src/DataManagement/mitkVectorProperty.cpp index d2fa075f522..f6e986629fc 100644 --- a/Modules/Core/src/DataManagement/mitkVectorProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkVectorProperty.cpp @@ -11,6 +11,7 @@ found in the LICENSE file. ============================================================================*/ #include "mitkVectorProperty.h" +#include namespace mitk { @@ -81,6 +82,20 @@ namespace mitk return m_PropertyContent; } + template + bool VectorProperty::ToJSON(nlohmann::json& j) const + { + j = this->GetValue(); + return true; + } + + template + bool VectorProperty::FromJSON(const nlohmann::json& j) + { + this->SetValue(j.get()); + return true; + } + // Explicit instantiation for defined types. MITK_DEFINE_VECTOR_PROPERTY(double) MITK_DEFINE_VECTOR_PROPERTY(int) diff --git a/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp b/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp index 33249b73cf4..e3b8f46fdeb 100644 --- a/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp @@ -67,6 +67,16 @@ void mitk::WeakPointerProperty::SetValue(const ValueType &value) SetWeakPointer(value.GetPointer()); } +bool mitk::WeakPointerProperty::ToJSON(nlohmann::json&) const +{ + return false; +} + +bool mitk::WeakPointerProperty::FromJSON(const nlohmann::json&) +{ + return false; +} + itk::LightObject::Pointer mitk::WeakPointerProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); diff --git a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp index ae79c6260b3..48db4613dff 100644 --- a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp +++ b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp @@ -164,17 +164,20 @@ class mitkTemporoSpatialStringPropertyTestSuite : public mitk::TestFixture void serializeTemporoSpatialStringPropertyToJSON() { - std::string data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refProp); - CPPUNIT_ASSERT(refJSON == - data); //"Testing serializeTemporoSpatialStringPropertyToJSON() producing correct string."); + auto data = nlohmann::json::parse(mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refProp)); + auto ref = nlohmann::json::parse(refJSON); - data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refPartlyCondensibleProp); - CPPUNIT_ASSERT(refPartlyCondensibleJSON == - data); + CPPUNIT_ASSERT(ref == data); //"Testing serializeTemporoSpatialStringPropertyToJSON() producing correct string."); - data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refCondensibleProp); - CPPUNIT_ASSERT(refCondensibleJSON == - data); + data = nlohmann::json::parse(mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refPartlyCondensibleProp)); + ref = nlohmann::json::parse(refPartlyCondensibleJSON); + + CPPUNIT_ASSERT(ref == data); + + data = nlohmann::json::parse(mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refCondensibleProp)); + ref = nlohmann::json::parse(refCondensibleJSON); + + CPPUNIT_ASSERT(ref == data); } void deserializeJSONToTemporoSpatialStringProperty() diff --git a/Modules/ModelFit/include/mitkScalarListLookupTable.h b/Modules/ModelFit/include/mitkScalarListLookupTable.h index 92c15417367..b7bed709109 100644 --- a/Modules/ModelFit/include/mitkScalarListLookupTable.h +++ b/Modules/ModelFit/include/mitkScalarListLookupTable.h @@ -16,6 +16,7 @@ found in the LICENSE file. #include #include #include +#include #include "MitkModelFitExports.h" @@ -86,6 +87,9 @@ namespace mitk */ MITKMODELFIT_EXPORT std::ostream& operator<<(std::ostream& stream, const ScalarListLookupTable& l); + + MITKMODELFIT_EXPORT void to_json(nlohmann::json& j, const ScalarListLookupTable& lut); + MITKMODELFIT_EXPORT void from_json(const nlohmann::json& j, ScalarListLookupTable& lut); } #endif diff --git a/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp b/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp index 35d1c336ac3..fa8c3b5a3b7 100644 --- a/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp +++ b/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp @@ -13,6 +13,21 @@ found in the LICENSE file. #include "mitkScalarListLookupTable.h" #include #include +#include + +namespace mitk +{ + void to_json(nlohmann::json& j, const ScalarListLookupTable& lut) + { + j = lut.GetLookupTable(); + } + + void from_json(const nlohmann::json& j, ScalarListLookupTable& lut) + { + lut.SetLookupTable(j.get()); + } +} + const char* mitk::ScalarListLookupTable::GetNameOfClass() const { return "ScalarListLookupTable"; diff --git a/Modules/RT/include/mitkIsoDoseLevelSetProperty.h b/Modules/RT/include/mitkIsoDoseLevelSetProperty.h index 32aa056beb1..84bce165a95 100644 --- a/Modules/RT/include/mitkIsoDoseLevelSetProperty.h +++ b/Modules/RT/include/mitkIsoDoseLevelSetProperty.h @@ -56,6 +56,9 @@ class MITKRT_EXPORT IsoDoseLevelSetProperty : public BaseProperty std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: diff --git a/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h b/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h index eadc300ac44..5887234c37c 100644 --- a/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h +++ b/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h @@ -56,6 +56,9 @@ class MITKRT_EXPORT IsoDoseLevelVectorProperty : public BaseProperty std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: diff --git a/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp b/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp index aa158348385..35b321b3ab1 100644 --- a/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp +++ b/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp @@ -96,6 +96,16 @@ std::string mitk::IsoDoseLevelSetProperty::GetValueAsString() const return myStr.str(); } +bool mitk::IsoDoseLevelSetProperty::ToJSON(nlohmann::json&) const +{ + return false; // Not implemented +} + +bool mitk::IsoDoseLevelSetProperty::FromJSON(const nlohmann::json&) +{ + return false; // Not implemented +} + itk::LightObject::Pointer mitk::IsoDoseLevelSetProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); diff --git a/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp b/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp index 0182f7c1352..808acfa397b 100644 --- a/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp +++ b/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp @@ -96,6 +96,16 @@ std::string mitk::IsoDoseLevelVectorProperty::GetValueAsString() const return myStr.str(); } +bool mitk::IsoDoseLevelVectorProperty::ToJSON(nlohmann::json&) const +{ + return false; // Not implemented +} + +bool mitk::IsoDoseLevelVectorProperty::FromJSON(const nlohmann::json&) +{ + return false; // Not implemented +} + itk::LightObject::Pointer mitk::IsoDoseLevelVectorProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); From 52f2b127c9540a516d8c79b202c67089e9d3d2ba Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 19 Oct 2023 17:17:48 +0200 Subject: [PATCH 06/92] Add missing includes --- Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp | 1 + Modules/Core/src/Rendering/mitkAnnotation.cpp | 5 +++-- Modules/Multilabel/mitkLabel.h | 1 + Modules/SceneSerialization/src/mitkSceneIO.cpp | 1 + Modules/SceneSerialization/src/mitkSceneReaderV1.h | 3 ++- 5 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp index c3df456fad5..fb77ebdb612 100644 --- a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp +++ b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp @@ -27,6 +27,7 @@ found in the LICENSE file. #include #include #include +#include // VTK Testing to compare the rendered image pixel-wise against a reference screen shot #include "vtkTesting.h" diff --git a/Modules/Core/src/Rendering/mitkAnnotation.cpp b/Modules/Core/src/Rendering/mitkAnnotation.cpp index c9ac002908b..9568fc05410 100644 --- a/Modules/Core/src/Rendering/mitkAnnotation.cpp +++ b/Modules/Core/src/Rendering/mitkAnnotation.cpp @@ -10,8 +10,9 @@ found in the LICENSE file. ============================================================================*/ -#include "mitkAnnotation.h" -#include "usGetModuleContext.h" +#include +#include +#include const std::string mitk::Annotation::US_INTERFACE_NAME = "org.mitk.services.Annotation"; const std::string mitk::Annotation::US_PROPKEY_AnnotationNAME = US_INTERFACE_NAME + ".name"; diff --git a/Modules/Multilabel/mitkLabel.h b/Modules/Multilabel/mitkLabel.h index 0d64a124df6..968452be5de 100644 --- a/Modules/Multilabel/mitkLabel.h +++ b/Modules/Multilabel/mitkLabel.h @@ -16,6 +16,7 @@ found in the LICENSE file. #include "MitkMultilabelExports.h" #include #include +#include #include namespace mitk diff --git a/Modules/SceneSerialization/src/mitkSceneIO.cpp b/Modules/SceneSerialization/src/mitkSceneIO.cpp index c96aa37a3ea..6072a7bc3f5 100644 --- a/Modules/SceneSerialization/src/mitkSceneIO.cpp +++ b/Modules/SceneSerialization/src/mitkSceneIO.cpp @@ -27,6 +27,7 @@ found in the LICENSE file. #include "mitkStandaloneDataStorage.h" #include #include +#include #include diff --git a/Modules/SceneSerialization/src/mitkSceneReaderV1.h b/Modules/SceneSerialization/src/mitkSceneReaderV1.h index ba69c6081e3..2c370b1ad8b 100644 --- a/Modules/SceneSerialization/src/mitkSceneReaderV1.h +++ b/Modules/SceneSerialization/src/mitkSceneReaderV1.h @@ -13,7 +13,8 @@ found in the LICENSE file. #ifndef mitkSceneReaderV1_h #define mitkSceneReaderV1_h -#include "mitkSceneReader.h" +#include +#include namespace tinyxml2 { From 2b0e0e3ecc29497388568b73e8e5e2500e413df1 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 19 Oct 2023 17:19:07 +0200 Subject: [PATCH 07/92] Remove empty files --- Modules/Core/files.cmake | 3 --- Modules/Core/src/DataManagement/mitkLine.cpp | 13 ------------- Modules/Core/src/DataManagement/mitkVector.cpp | 13 ------------- Modules/Core/src/IO/mitkImageGenerator.cpp | 11 ----------- 4 files changed, 40 deletions(-) delete mode 100644 Modules/Core/src/DataManagement/mitkLine.cpp delete mode 100644 Modules/Core/src/DataManagement/mitkVector.cpp delete mode 100644 Modules/Core/src/IO/mitkImageGenerator.cpp diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index d8412455d1a..5968a742879 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -108,7 +108,6 @@ set(CPP_FILES DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp - DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable @@ -174,7 +173,6 @@ set(CPP_FILES DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp - DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp @@ -240,7 +238,6 @@ set(CPP_FILES IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp - IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp diff --git a/Modules/Core/src/DataManagement/mitkLine.cpp b/Modules/Core/src/DataManagement/mitkLine.cpp deleted file mode 100644 index 4d6ae904bc5..00000000000 --- a/Modules/Core/src/DataManagement/mitkLine.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/*============================================================================ - -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 "mitkLine.h" diff --git a/Modules/Core/src/DataManagement/mitkVector.cpp b/Modules/Core/src/DataManagement/mitkVector.cpp deleted file mode 100644 index a9ca6ccfb5b..00000000000 --- a/Modules/Core/src/DataManagement/mitkVector.cpp +++ /dev/null @@ -1,13 +0,0 @@ -/*============================================================================ - -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 "mitkVector.h" diff --git a/Modules/Core/src/IO/mitkImageGenerator.cpp b/Modules/Core/src/IO/mitkImageGenerator.cpp deleted file mode 100644 index f92933ddebd..00000000000 --- a/Modules/Core/src/IO/mitkImageGenerator.cpp +++ /dev/null @@ -1,11 +0,0 @@ -/*============================================================================ - -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. - -============================================================================*/ From d039aaa6d65b731fd7512a9077f479aff6384da7 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 19 Oct 2023 17:20:05 +0200 Subject: [PATCH 08/92] Remove undefined method declaration --- Modules/DataTypesExt/include/mitkBoundingObject.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Modules/DataTypesExt/include/mitkBoundingObject.h b/Modules/DataTypesExt/include/mitkBoundingObject.h index 9a7c6128a70..9487df20375 100644 --- a/Modules/DataTypesExt/include/mitkBoundingObject.h +++ b/Modules/DataTypesExt/include/mitkBoundingObject.h @@ -48,8 +48,6 @@ namespace mitk BoundingObject(); ~BoundingObject() override; - bool WriteXMLData(XMLWriter &xmlWriter); - //##Documentation //## \brief If \a true, the Boundingobject describes a positive volume, //## if \a false a negative volume. From 993e4d08d5f3e8cce6c5e6137edcdf2227c8e186 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 19 Oct 2023 17:22:28 +0200 Subject: [PATCH 09/92] Implement property list JSON serialization --- Modules/Core/files.cmake | 4 +- Modules/Core/include/mitkCoreServices.h | 8 + .../include/mitkIPropertyDeserialization.h | 37 +++++ .../include/mitkPropertyDeserialization.h | 39 +++++ Modules/Core/include/mitkPropertyList.h | 17 +- .../mitkIPropertyDeserialization.cpp | 17 ++ .../mitkPropertyDeserialization.cpp | 38 +++++ .../src/DataManagement/mitkPropertyList.cpp | 56 ++++++- Modules/Core/src/mitkCoreActivator.cpp | 151 +++++++++++++----- Modules/Core/src/mitkCoreActivator.h | 2 + Modules/Core/src/mitkCoreServices.cpp | 6 + 11 files changed, 316 insertions(+), 59 deletions(-) create mode 100644 Modules/Core/include/mitkIPropertyDeserialization.h create mode 100644 Modules/Core/include/mitkPropertyDeserialization.h create mode 100644 Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp create mode 100644 Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 5968a742879..1aa29e20757 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -97,6 +97,7 @@ set(CPP_FILES DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp + DataManagement/mitkIPropertyDeserialization.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp @@ -110,7 +111,7 @@ set(CPP_FILES DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp - DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable + DataManagement/mitkLookupTables.cpp DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp @@ -140,6 +141,7 @@ set(CPP_FILES DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp + DataManagement/mitkPropertyDeserialization.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp diff --git a/Modules/Core/include/mitkCoreServices.h b/Modules/Core/include/mitkCoreServices.h index ce302014413..f02e895e099 100644 --- a/Modules/Core/include/mitkCoreServices.h +++ b/Modules/Core/include/mitkCoreServices.h @@ -30,6 +30,7 @@ namespace mitk struct IMimeTypeProvider; class IPropertyAliases; class IPropertyDescriptions; + class IPropertyDeserialization; class IPropertyExtensions; class IPropertyFilters; class IPropertyPersistence; @@ -72,6 +73,13 @@ namespace mitk */ static IPropertyDescriptions *GetPropertyDescriptions(us::ModuleContext *context = us::GetModuleContext()); + /** + * @brief Get an IPropertyDeserialization instance. + * @param context The module context of the module getting the service. + * @return A non-nullptr IPropertyDeserialization instance. + */ + static IPropertyDeserialization* GetPropertyDeserialization(us::ModuleContext* context = us::GetModuleContext()); + /** * @brief Get an IPropertyExtensions instance. * @param context The module context of the module getting the service. diff --git a/Modules/Core/include/mitkIPropertyDeserialization.h b/Modules/Core/include/mitkIPropertyDeserialization.h new file mode 100644 index 00000000000..c700a5bfde9 --- /dev/null +++ b/Modules/Core/include/mitkIPropertyDeserialization.h @@ -0,0 +1,37 @@ +/*============================================================================ + +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 mitkIPropertyDeserialization_h +#define mitkIPropertyDeserialization_h + +#include +#include +#include +#include + +namespace mitk +{ + class BaseProperty; + + class MITKCORE_EXPORT IPropertyDeserialization + { + public: + virtual ~IPropertyDeserialization(); + + virtual void RegisterProperty(const BaseProperty* property) = 0; + virtual itk::SmartPointer CreateInstance(const std::string& className) = 0; + }; +} + +MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyDeserialization, "org.mitk.IPropertyDeserialization") + +#endif diff --git a/Modules/Core/include/mitkPropertyDeserialization.h b/Modules/Core/include/mitkPropertyDeserialization.h new file mode 100644 index 00000000000..c5e3f6860df --- /dev/null +++ b/Modules/Core/include/mitkPropertyDeserialization.h @@ -0,0 +1,39 @@ +/*============================================================================ + +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 mitkPropertyDeserialization_h +#define mitkPropertyDeserialization_h + +#include +#include + +namespace mitk +{ + class PropertyDeserialization : public IPropertyDeserialization + { + public: + PropertyDeserialization(); + virtual ~PropertyDeserialization(); + + PropertyDeserialization(const PropertyDeserialization&) = delete; + PropertyDeserialization& operator=(const PropertyDeserialization&) = delete; + + void RegisterProperty(const BaseProperty* property) override; + itk::SmartPointer CreateInstance(const std::string& className) override; + + private: + using MapType = std::map>; + MapType m_Map; + }; +} + +#endif diff --git a/Modules/Core/include/mitkPropertyList.h b/Modules/Core/include/mitkPropertyList.h index 40f05857cfe..996e7c10bae 100644 --- a/Modules/Core/include/mitkPropertyList.h +++ b/Modules/Core/include/mitkPropertyList.h @@ -13,21 +13,11 @@ found in the LICENSE file. #ifndef mitkPropertyList_h #define mitkPropertyList_h -#include "mitkBaseProperty.h" -#include "mitkGenericProperty.h" -#include "mitkUIDGenerator.h" -#include "mitkIPropertyOwner.h" -#include - -#include - -#include -#include +#include +#include namespace mitk { - class XMLWriter; - /** * @brief Key-value list holding instances of BaseProperty * @@ -234,6 +224,9 @@ namespace mitk bool IsEmpty() const { return m_Properties.empty(); } virtual void Clear(); + void ToJSON(nlohmann::json& j) const; + void FromJSON(const nlohmann::json& j); + protected: PropertyList(); PropertyList(const PropertyList &other); diff --git a/Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp b/Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp new file mode 100644 index 00000000000..bb3e4b2c124 --- /dev/null +++ b/Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp @@ -0,0 +1,17 @@ +/*============================================================================ + +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 "mitkIPropertyDeserialization.h" + +mitk::IPropertyDeserialization::~IPropertyDeserialization() +{ +} diff --git a/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp b/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp new file mode 100644 index 00000000000..b95de60e5e9 --- /dev/null +++ b/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp @@ -0,0 +1,38 @@ +/*============================================================================ + +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 +#include + +mitk::PropertyDeserialization::PropertyDeserialization() +{ +} + +mitk::PropertyDeserialization::~PropertyDeserialization() +{ +} + +void mitk::PropertyDeserialization::RegisterProperty(const BaseProperty* property) +{ + if (property != nullptr) + m_Map[property->GetNameOfClass()] = static_cast(property->CreateAnother().GetPointer()); +} + +mitk::BaseProperty::Pointer mitk::PropertyDeserialization::CreateInstance(const std::string& className) +{ + auto it = m_Map.find(className); + + if (it != m_Map.end()) + return static_cast(it->second->CreateAnother().GetPointer()); + + return nullptr; +} diff --git a/Modules/Core/src/DataManagement/mitkPropertyList.cpp b/Modules/Core/src/DataManagement/mitkPropertyList.cpp index d15eac3f6e8..88d5d7e34c6 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyList.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyList.cpp @@ -10,11 +10,11 @@ found in the LICENSE file. ============================================================================*/ -#include "mitkPropertyList.h" - -#include "mitkNumericTypes.h" -#include "mitkProperties.h" -#include "mitkStringProperty.h" +#include +#include +#include +#include +#include mitk::BaseProperty::ConstPointer mitk::PropertyList::GetConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) const { @@ -374,3 +374,49 @@ void mitk::PropertyList::SetDoubleProperty(const char *propertyKey, double doubl { SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); } + +void mitk::PropertyList::ToJSON(nlohmann::json& j) const +{ + j = nlohmann::json::object(); + + for (const auto& [name, property] : m_Properties) + { + nlohmann::json serializedProperty; + + if (property->ToJSON(serializedProperty)) + j[property->GetNameOfClass()][name] = serializedProperty; + } + + auto test = PropertyList::New(); + test->FromJSON(j); +} + +void mitk::PropertyList::FromJSON(const nlohmann::json& j) +{ + mitk::CoreServicePointer service(mitk::CoreServices::GetPropertyDeserialization()); + PropertyMap properties; + + auto jObject = j.get(); + + for (const auto& [type, serializedProperties] : jObject) + { + auto prototype = service->CreateInstance(type); + + if (prototype.IsNull()) + { + MITK_ERROR << "Cannot create instance(s) of class \"" << type << "\"!"; + continue; + } + + auto serializedPropertiesObject = serializedProperties.get(); + + for (const auto& [name, serializedProperty] : serializedPropertiesObject) + { + BaseProperty::Pointer property = static_cast(prototype->CreateAnother().GetPointer()); + property->FromJSON(serializedProperty); + properties[name] = property; + } + } + + m_Properties = properties; +} diff --git a/Modules/Core/src/mitkCoreActivator.cpp b/Modules/Core/src/mitkCoreActivator.cpp index 594cdf54909..0fc801eec61 100644 --- a/Modules/Core/src/mitkCoreActivator.cpp +++ b/Modules/Core/src/mitkCoreActivator.cpp @@ -53,6 +53,27 @@ found in the LICENSE file. #include #include +// Properties +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + // ITK "injects" static initialization code for IO factories // via the itkImageIOFactoryRegisterManager.h header (which // is generated in the application library build directory). @@ -61,10 +82,12 @@ found in the LICENSE file. // method), we include the ITK header here. #include -void HandleMicroServicesMessages(us::MsgType type, const char *msg) +namespace { - switch (type) + void HandleMicroServicesMessages(us::MsgType type, const char* msg) { + switch (type) + { case us::DebugMsg: MITK_DEBUG << msg; break; @@ -77,59 +100,100 @@ void HandleMicroServicesMessages(us::MsgType type, const char *msg) case us::ErrorMsg: MITK_ERROR << msg; break; + } } -} -void AddMitkAutoLoadPaths(const std::string &programPath) -{ - us::ModuleSettings::AddAutoLoadPath(programPath); -#ifdef __APPLE__ - // Walk up three directories since that is where the .dylib files are located - // for build trees. - std::string additionalPath = programPath; - bool addPath = true; - for (int i = 0; i < 3; ++i) + void AddMitkAutoLoadPaths(const std::string& programPath) { - std::size_t index = additionalPath.find_last_of('/'); - if (index != std::string::npos) + us::ModuleSettings::AddAutoLoadPath(programPath); +#ifdef __APPLE__ + // Walk up three directories since that is where the .dylib files are located + // for build trees. + std::string additionalPath = programPath; + bool addPath = true; + for (int i = 0; i < 3; ++i) { - additionalPath = additionalPath.substr(0, index); + std::size_t index = additionalPath.find_last_of('/'); + if (index != std::string::npos) + { + additionalPath = additionalPath.substr(0, index); + } + else + { + addPath = false; + break; + } } - else + if (addPath) { - addPath = false; - break; + us::ModuleSettings::AddAutoLoadPath(additionalPath); } +#endif } - if (addPath) + + void AddPropertyPersistence(const mitk::PropertyKeyPath& propPath) { - us::ModuleSettings::AddAutoLoadPath(additionalPath); - } -#endif -} + mitk::CoreServicePointer persistenceService(mitk::CoreServices::GetPropertyPersistence()); -void AddPropertyPersistence(const mitk::PropertyKeyPath& propPath) -{ - mitk::CoreServicePointer persistenceService(mitk::CoreServices::GetPropertyPersistence()); + auto info = mitk::PropertyPersistenceInfo::New(); + if (propPath.IsExplicit()) + { + std::string name = mitk::PropertyKeyPathToPropertyName(propPath); + std::string key = name; + std::replace(key.begin(), key.end(), '.', '_'); + info->SetNameAndKey(name, key); + } + else + { + std::string key = mitk::PropertyKeyPathToPersistenceKeyRegEx(propPath); + std::string keyTemplate = mitk::PropertyKeyPathToPersistenceKeyTemplate(propPath); + std::string propRegEx = mitk::PropertyKeyPathToPropertyRegEx(propPath); + std::string propTemplate = mitk::PropertyKeyPathToPersistenceNameTemplate(propPath); + info->UseRegEx(propRegEx, propTemplate, key, keyTemplate); + } - auto info = mitk::PropertyPersistenceInfo::New(); - if (propPath.IsExplicit()) - { - std::string name = mitk::PropertyKeyPathToPropertyName(propPath); - std::string key = name; - std::replace(key.begin(), key.end(), '.', '_'); - info->SetNameAndKey(name, key); + persistenceService->AddInfo(info); } - else + + void RegisterProperties() { - std::string key = mitk::PropertyKeyPathToPersistenceKeyRegEx(propPath); - std::string keyTemplate = mitk::PropertyKeyPathToPersistenceKeyTemplate(propPath); - std::string propRegEx = mitk::PropertyKeyPathToPropertyRegEx(propPath); - std::string propTemplate = mitk::PropertyKeyPathToPersistenceNameTemplate(propPath); - info->UseRegEx(propRegEx, propTemplate, key, keyTemplate); + mitk::CoreServicePointer service(mitk::CoreServices::GetPropertyDeserialization()); + + service->RegisterProperty(mitk::AnnotationProperty::New()); + service->RegisterProperty(mitk::ClippingProperty::New()); + service->RegisterProperty(mitk::ColorProperty::New()); + service->RegisterProperty(mitk::BoolProperty::New()); + service->RegisterProperty(mitk::BoolLookupTableProperty::New()); + service->RegisterProperty(mitk::DoubleProperty::New()); + service->RegisterProperty(mitk::DoubleVectorProperty::New()); + service->RegisterProperty(mitk::FloatProperty::New()); + service->RegisterProperty(mitk::FloatLookupTableProperty::New()); + service->RegisterProperty(mitk::GroupTagProperty::New()); + service->RegisterProperty(mitk::IntProperty::New()); + service->RegisterProperty(mitk::IntLookupTableProperty::New()); + service->RegisterProperty(mitk::IntVectorProperty::New()); + service->RegisterProperty(mitk::LevelWindowProperty::New()); + service->RegisterProperty(mitk::LookupTableProperty::New()); + service->RegisterProperty(mitk::PlaneOrientationProperty::New()); + service->RegisterProperty(mitk::Point2dProperty::New()); + service->RegisterProperty(mitk::Point3dProperty::New()); + service->RegisterProperty(mitk::Point3iProperty::New()); + service->RegisterProperty(mitk::Point4dProperty::New()); + service->RegisterProperty(mitk::PointSetShapeProperty::New()); + service->RegisterProperty(mitk::ModalityProperty::New()); + service->RegisterProperty(mitk::RenderingModeProperty::New()); + service->RegisterProperty(mitk::StringProperty::New()); + service->RegisterProperty(mitk::StringLookupTableProperty::New()); + service->RegisterProperty(mitk::TemporoSpatialStringProperty::New()); + service->RegisterProperty(mitk::TransferFunctionProperty::New()); + service->RegisterProperty(mitk::UIntProperty::New()); + service->RegisterProperty(mitk::UShortProperty::New()); + service->RegisterProperty(mitk::Vector3DProperty::New()); + service->RegisterProperty(mitk::VtkInterpolationProperty::New()); + service->RegisterProperty(mitk::VtkRepresentationProperty::New()); + service->RegisterProperty(mitk::VtkResliceInterpolationProperty::New()); + service->RegisterProperty(mitk::VtkScalarModeProperty::New()); } - - persistenceService->AddInfo(info); } class FixedNiftiImageIO : public itk::NiftiImageIO @@ -182,6 +246,9 @@ void MitkCoreActivator::Load(us::ModuleContext *context) m_PropertyDescriptions.reset(new mitk::PropertyDescriptions); context->RegisterService(m_PropertyDescriptions.get()); + m_PropertyDeserialization.reset(new mitk::PropertyDeserialization); + context->RegisterService(m_PropertyDeserialization.get()); + m_PropertyExtensions.reset(new mitk::PropertyExtensions); context->RegisterService(m_PropertyExtensions.get()); @@ -225,6 +292,8 @@ void MitkCoreActivator::Load(us::ModuleContext *context) AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIRuleIDPropertyKeyPath()); AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIPropertyKeyPath("","").AddAnyElement()); + RegisterProperties(); + /* There IS an option to exchange ALL vtkTexture instances against vtkNeverTranslucentTextureFactory. This code is left here as a reminder, just in case we might need to do that some time. diff --git a/Modules/Core/src/mitkCoreActivator.h b/Modules/Core/src/mitkCoreActivator.h index 19c513bba7f..d28f97a70a2 100644 --- a/Modules/Core/src/mitkCoreActivator.h +++ b/Modules/Core/src/mitkCoreActivator.h @@ -22,6 +22,7 @@ found in the LICENSE file. #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ class MitkCoreActivator : public us::ModuleActivator std::unique_ptr m_PlanePositionManager; std::unique_ptr m_PropertyAliases; std::unique_ptr m_PropertyDescriptions; + std::unique_ptr m_PropertyDeserialization; std::unique_ptr m_PropertyExtensions; std::unique_ptr m_PropertyFilters; std::unique_ptr m_PropertyPersistence; diff --git a/Modules/Core/src/mitkCoreServices.cpp b/Modules/Core/src/mitkCoreServices.cpp index 720730347f7..96dce2bcde9 100644 --- a/Modules/Core/src/mitkCoreServices.cpp +++ b/Modules/Core/src/mitkCoreServices.cpp @@ -15,6 +15,7 @@ found in the LICENSE file. #include #include #include +#include #include #include #include @@ -74,6 +75,11 @@ namespace mitk return GetCoreService(context); } + IPropertyDeserialization* CoreServices::GetPropertyDeserialization(us::ModuleContext* context) + { + return GetCoreService(context); + } + IPropertyExtensions *CoreServices::GetPropertyExtensions(us::ModuleContext *context) { return GetCoreService(context); From dc78bede64250ebfb36fb3a4b230cda5299fd683 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 19 Oct 2023 17:23:33 +0200 Subject: [PATCH 10/92] Fix serialization of wrongly used generic properties --- Modules/Core/include/mitkGenericProperty.h | 31 +++++++++++++++------- 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/Modules/Core/include/mitkGenericProperty.h b/Modules/Core/include/mitkGenericProperty.h index 1f2cecc5614..b95907e77be 100644 --- a/Modules/Core/include/mitkGenericProperty.h +++ b/Modules/Core/include/mitkGenericProperty.h @@ -36,9 +36,10 @@ namespace mitk - an operator<< so that the properties value can be put into a std::stringstream - an operator== so that two properties can be checked for equality - Note: you must use the macro mitkSpecializeGenericProperty to provide specializations - for concrete types (e.g. BoolProperty). Please see mitkProperties.h for examples. If you - don't use the mitkSpecializeGenericProperty Macro, GetNameOfClass() returns a wrong name. + Note: you must use the macros mitkDeclareGenericProperty and mitkDefineGenericProperty to + provide specializations for concrete types (e.g. BoolProperty). See mitkProperties.h for + examples. If you don't use these macros, GetNameOfClass() will return "GenericProperty", + which will mess up serialization for example. */ template @@ -61,16 +62,14 @@ namespace mitk return myStr.str(); } - bool ToJSON(nlohmann::json& j) const override + bool ToJSON(nlohmann::json&) const override { - j = this->GetValue(); - return true; + return false; } - bool FromJSON(const nlohmann::json& j) override + bool FromJSON(const nlohmann::json&) override { - this->SetValue(j.get()); - return true; + return false; } using BaseProperty::operator=; @@ -129,6 +128,9 @@ namespace mitk itkCloneMacro(Self); \ mitkNewMacro1Param(PropertyName, Type); \ \ + bool ToJSON(nlohmann::json& j) const override; \ + bool FromJSON(const nlohmann::json& j) override; \ + \ using BaseProperty::operator=; \ \ protected: \ @@ -149,6 +151,17 @@ namespace mitk itk::LightObject::Pointer result(new Self(*this)); \ result->UnRegister(); \ return result; \ + } \ + bool mitk::PropertyName::ToJSON(nlohmann::json& j) const \ + { \ + j = this->GetValue(); \ + return true; \ + } \ + \ + bool mitk::PropertyName::FromJSON(const nlohmann::json& j) \ + { \ + this->SetValue(j.get()); \ + return true; \ } #endif From 2765ccf892a9acb1fd1331e2651db82df8cd7a43 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 19 Oct 2023 17:23:49 +0200 Subject: [PATCH 11/92] Improve syntax --- .../mitkTemporoSpatialStringProperty.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp index 6a2eeb4a091..4764497413c 100644 --- a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp @@ -356,17 +356,17 @@ bool mitk::TemporoSpatialStringProperty::ToJSON(nlohmann::json& j) const const auto& maxTimePointID = t.first.second; auto value = nlohmann::json::object(); - value.push_back({"z", minSliceID}); + value["z"] = minSliceID; if (minSliceID != maxSliceID) - value.push_back({"zmax", maxSliceID}); + value["zmax"] = maxSliceID; - value.push_back({"t", minTimePointID}); + value["t"] = minTimePointID; if (minTimePointID != maxTimePointID) - value.push_back({"tmax", maxTimePointID}); + value["tmax"] = maxTimePointID; - value.push_back({"value", t.second}); + value["value"] = t.second; values.push_back(value); } From 7a8b1d30dbd0ca174cee504ea9674c30b79db433 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 19 Oct 2023 17:24:48 +0200 Subject: [PATCH 12/92] Fix deserialization of LookupTableProperty --- .../mitkLookupTableProperty.cpp | 21 +++++++++---------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp index 63751f8bfa1..43bf40cf23d 100644 --- a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp @@ -83,17 +83,16 @@ bool mitk::LookupTableProperty::ToJSON(nlohmann::json& j) const table.push_back(value); } - j = nlohmann::json{{ - { "NumberOfColors", static_cast(lut->GetNumberOfTableValues()) }, - { "Scale", lut->GetScale() }, - { "Ramp", lut->GetRamp() }, - { "HueRange", nlohmann::json::array({ lut->GetHueRange()[0], lut->GetHueRange()[1] }) }, - { "ValueRange", nlohmann::json::array({ lut->GetValueRange()[0], lut->GetValueRange()[1] }) }, - { "SaturationRange", nlohmann::json::array({ lut->GetSaturationRange()[0], lut->GetSaturationRange()[1] }) }, - { "AlphaRange", nlohmann::json::array({ lut->GetAlphaRange()[0], lut->GetAlphaRange()[1] }) }, - { "TableRange", nlohmann::json::array({ lut->GetTableRange()[0], lut->GetTableRange()[1] }) }, - { "Table", table } - }}; + j = nlohmann::json::object(); + j["NumberOfColors"] = static_cast(lut->GetNumberOfTableValues()); + j["Scale"] = lut->GetScale(); + j["Ramp"] = lut->GetRamp(); + j["HueRange"] = nlohmann::json::array({ lut->GetHueRange()[0], lut->GetHueRange()[1] }); + j["ValueRange"] = nlohmann::json::array({ lut->GetValueRange()[0], lut->GetValueRange()[1] }); + j["SaturationRange"] = nlohmann::json::array({ lut->GetSaturationRange()[0], lut->GetSaturationRange()[1] }); + j["AlphaRange"] = nlohmann::json::array({ lut->GetAlphaRange()[0], lut->GetAlphaRange()[1] }); + j["TableRange"] = nlohmann::json::array({ lut->GetTableRange()[0], lut->GetTableRange()[1] }); + j["Table"] = table; return true; } From 0d9f69800c543c48c1a8fec24df2c36feb945ced Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 20 Oct 2023 08:28:41 +0200 Subject: [PATCH 13/92] Improve property registration for deserialization --- .../include/mitkIPropertyDeserialization.h | 21 ++++-- .../include/mitkPropertyDeserialization.h | 6 +- .../mitkPropertyDeserialization.cpp | 11 ++- Modules/Core/src/mitkCoreActivator.cpp | 68 +++++++++---------- 4 files changed, 59 insertions(+), 47 deletions(-) diff --git a/Modules/Core/include/mitkIPropertyDeserialization.h b/Modules/Core/include/mitkIPropertyDeserialization.h index c700a5bfde9..8d1a43b8c28 100644 --- a/Modules/Core/include/mitkIPropertyDeserialization.h +++ b/Modules/Core/include/mitkIPropertyDeserialization.h @@ -13,22 +13,33 @@ found in the LICENSE file. #ifndef mitkIPropertyDeserialization_h #define mitkIPropertyDeserialization_h -#include +#include #include +#include + #include + #include +#include namespace mitk { - class BaseProperty; - class MITKCORE_EXPORT IPropertyDeserialization { public: virtual ~IPropertyDeserialization(); - virtual void RegisterProperty(const BaseProperty* property) = 0; - virtual itk::SmartPointer CreateInstance(const std::string& className) = 0; + virtual BaseProperty::Pointer CreateInstance(const std::string& className) = 0; + + template >> + void RegisterProperty() + { + this->InternalRegisterProperty(T::New()); + } + + protected: + + virtual void InternalRegisterProperty(const BaseProperty* property) = 0; }; } diff --git a/Modules/Core/include/mitkPropertyDeserialization.h b/Modules/Core/include/mitkPropertyDeserialization.h index c5e3f6860df..274bb1d22fd 100644 --- a/Modules/Core/include/mitkPropertyDeserialization.h +++ b/Modules/Core/include/mitkPropertyDeserialization.h @@ -27,11 +27,13 @@ namespace mitk PropertyDeserialization(const PropertyDeserialization&) = delete; PropertyDeserialization& operator=(const PropertyDeserialization&) = delete; - void RegisterProperty(const BaseProperty* property) override; itk::SmartPointer CreateInstance(const std::string& className) override; + protected: + void InternalRegisterProperty(const BaseProperty* property) override; + private: - using MapType = std::map>; + using MapType = std::map; MapType m_Map; }; } diff --git a/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp b/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp index b95de60e5e9..251f2b9ff6d 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp @@ -21,12 +21,6 @@ mitk::PropertyDeserialization::~PropertyDeserialization() { } -void mitk::PropertyDeserialization::RegisterProperty(const BaseProperty* property) -{ - if (property != nullptr) - m_Map[property->GetNameOfClass()] = static_cast(property->CreateAnother().GetPointer()); -} - mitk::BaseProperty::Pointer mitk::PropertyDeserialization::CreateInstance(const std::string& className) { auto it = m_Map.find(className); @@ -36,3 +30,8 @@ mitk::BaseProperty::Pointer mitk::PropertyDeserialization::CreateInstance(const return nullptr; } + +void mitk::PropertyDeserialization::InternalRegisterProperty(const BaseProperty* property) +{ + m_Map[property->GetNameOfClass()] = property; +} diff --git a/Modules/Core/src/mitkCoreActivator.cpp b/Modules/Core/src/mitkCoreActivator.cpp index 0fc801eec61..a5c00479ad4 100644 --- a/Modules/Core/src/mitkCoreActivator.cpp +++ b/Modules/Core/src/mitkCoreActivator.cpp @@ -159,40 +159,40 @@ namespace { mitk::CoreServicePointer service(mitk::CoreServices::GetPropertyDeserialization()); - service->RegisterProperty(mitk::AnnotationProperty::New()); - service->RegisterProperty(mitk::ClippingProperty::New()); - service->RegisterProperty(mitk::ColorProperty::New()); - service->RegisterProperty(mitk::BoolProperty::New()); - service->RegisterProperty(mitk::BoolLookupTableProperty::New()); - service->RegisterProperty(mitk::DoubleProperty::New()); - service->RegisterProperty(mitk::DoubleVectorProperty::New()); - service->RegisterProperty(mitk::FloatProperty::New()); - service->RegisterProperty(mitk::FloatLookupTableProperty::New()); - service->RegisterProperty(mitk::GroupTagProperty::New()); - service->RegisterProperty(mitk::IntProperty::New()); - service->RegisterProperty(mitk::IntLookupTableProperty::New()); - service->RegisterProperty(mitk::IntVectorProperty::New()); - service->RegisterProperty(mitk::LevelWindowProperty::New()); - service->RegisterProperty(mitk::LookupTableProperty::New()); - service->RegisterProperty(mitk::PlaneOrientationProperty::New()); - service->RegisterProperty(mitk::Point2dProperty::New()); - service->RegisterProperty(mitk::Point3dProperty::New()); - service->RegisterProperty(mitk::Point3iProperty::New()); - service->RegisterProperty(mitk::Point4dProperty::New()); - service->RegisterProperty(mitk::PointSetShapeProperty::New()); - service->RegisterProperty(mitk::ModalityProperty::New()); - service->RegisterProperty(mitk::RenderingModeProperty::New()); - service->RegisterProperty(mitk::StringProperty::New()); - service->RegisterProperty(mitk::StringLookupTableProperty::New()); - service->RegisterProperty(mitk::TemporoSpatialStringProperty::New()); - service->RegisterProperty(mitk::TransferFunctionProperty::New()); - service->RegisterProperty(mitk::UIntProperty::New()); - service->RegisterProperty(mitk::UShortProperty::New()); - service->RegisterProperty(mitk::Vector3DProperty::New()); - service->RegisterProperty(mitk::VtkInterpolationProperty::New()); - service->RegisterProperty(mitk::VtkRepresentationProperty::New()); - service->RegisterProperty(mitk::VtkResliceInterpolationProperty::New()); - service->RegisterProperty(mitk::VtkScalarModeProperty::New()); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); + service->RegisterProperty(); } } From f99f7f7b5bf373019c1cb89d40aa949504843109 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Mon, 23 Oct 2023 14:08:22 +0200 Subject: [PATCH 14/92] Simplify reader --- Modules/ROI/autoload/IO/src/mitkROIIO.cpp | 84 ++++++++++------------- 1 file changed, 37 insertions(+), 47 deletions(-) diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp index 6bdce335bbd..4be0e0d2b79 100644 --- a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -19,6 +19,33 @@ found in the LICENSE file. #include #include +namespace +{ + mitk::Geometry3D::Pointer ReadGeometry(const nlohmann::json& jsonGeometry) + { + auto result = mitk::Geometry3D::New(); + result->ImageGeometryOn(); + + if (!jsonGeometry.is_object()) + mitkThrow() << "Geometry is expected to be a JSON object."; + + if (jsonGeometry.contains("Origin")) + result->SetOrigin(jsonGeometry["Origin"].get()); + + if (jsonGeometry.contains("Spacing")) + result->SetSpacing(jsonGeometry["Spacing"].get()); + + if (jsonGeometry.contains("Size")) + { + auto size = jsonGeometry["Size"].get(); + mitk::BaseGeometry::BoundsArrayType bounds({ 0.0, size[0], 0.0, size[1], 0.0, size[2] }); + result->SetBounds(bounds); + } + + return result; + } +} + mitk::ROIIO::ROIIO() : AbstractFileIO(ROI::GetStaticNameOfClass(), MitkROIIOMimeTypes::ROI_MIMETYPE(), "MITK ROI") { @@ -45,67 +72,30 @@ std::vector mitk::ROIIO::DoRead() stream = &fileStream; } - nlohmann::json json; + auto result = ROI::New(); try { - json = nlohmann::json::parse(*stream); - } - catch (const nlohmann::json::exception &e) - { - mitkThrow() << e.what(); - } - - if (!json.is_object()) - mitkThrow() << "Unknown file format (expected JSON object as root)!"; - - if ("MITK ROI" != json.value("FileFormat", "")) - mitkThrow() << "Unknown file format (expected \"MITK ROI\")!"; - - if (1 != json.value("Version", 0)) - mitkThrow() << "Unknown file format version (expected \"1\")!"; - - auto geometry = Geometry3D::New(); + auto json = nlohmann::json::parse(*stream); - if (json.contains("Geometry")) - { - auto jsonGeometry = json["Geometry"]; + if ("MITK ROI" != json["FileFormat"].get()) + mitkThrow() << "Unknown file format (expected \"MITK ROI\")!"; - if (!jsonGeometry.is_object()) - mitkThrow() << "Geometry is expected to be a JSON object."; + if (1 != json["Version"].get()) + mitkThrow() << "Unknown file format version (expected version 1)!"; - auto geometryType = jsonGeometry.value("Type", "Embedded"); + result->SetGeometry(ReadGeometry(json["Geometry"])); - if (geometryType != "Embedded") - mitkThrow() << "Unknown geometry type \"" << geometryType << "\"!"; - - if (jsonGeometry.contains("Origin")) - geometry->SetOrigin(jsonGeometry["Origin"].get()); - - if (jsonGeometry.contains("Spacing")) - geometry->SetSpacing(jsonGeometry["Spacing"].get()); - } - - if (!json.contains("ROIs") || !json["ROIs"].is_array()) - mitkThrow() << "ROIs array not found!"; - - std::vector results; - - try - { - for (const auto& roi : json["ROIs"]) + for (const auto& jsonROI : json["ROIs"]) { - auto result = ROI::New(); - result->SetGeometry(geometry); - results.push_back(result.GetPointer()); } } - catch (const nlohmann::json::type_error &e) + catch (const nlohmann::json::exception &e) { mitkThrow() << e.what(); } - return results; + return { result }; } void mitk::ROIIO::Write() From 100ce51ec1d273d81a0f64af5fcb0fb59aa0d24a Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 27 Oct 2023 06:37:49 +0200 Subject: [PATCH 15/92] Add bounds and properties to ROI data --- Modules/ROI/include/mitkROI.h | 37 +++++++++++++++++ Modules/ROI/src/mitkROI.cpp | 76 +++++++++++++++++++++++++++++++++++ 2 files changed, 113 insertions(+) diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h index 8167f78cb96..fb5851e1d56 100644 --- a/Modules/ROI/include/mitkROI.h +++ b/Modules/ROI/include/mitkROI.h @@ -21,10 +21,44 @@ namespace mitk class MITKROI_EXPORT ROI : public BaseData { public: + struct MITKROI_EXPORT Element : IPropertyOwner + { + Element(); + ~Element() = default; + + BaseProperty::ConstPointer GetConstProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = true) const override; + std::vector GetPropertyKeys(const std::string& contextName = "", bool includeDefaultContext = false) const override; + std::vector GetPropertyContextNames() const override; + + BaseProperty* GetNonConstProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = true) override; + void SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; + void RemoveProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; + + Point3D Min; + Point3D Max; + PropertyList::Pointer Properties; + }; + mitkClassMacro(ROI, BaseData) itkFactorylessNewMacro(Self) itkCloneMacro(Self) + using ElementsType = std::vector; + using Iterator = ElementsType::iterator; + using ConstIterator = ElementsType::const_iterator; + + size_t GetNumberOfElements() const; + size_t AddElement(const Element& element); + + const Element* GetElement(size_t index) const; + Element* GetElement(size_t index); + + ConstIterator begin() const; + ConstIterator end() const; + + Iterator begin(); + Iterator end(); + void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; @@ -36,6 +70,9 @@ namespace mitk ROI(); ROI(const Self& other); ~ROI() override; + + private: + ElementsType m_Elements; }; } diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp index 91b5d905e2a..67f0540aa8f 100644 --- a/Modules/ROI/src/mitkROI.cpp +++ b/Modules/ROI/src/mitkROI.cpp @@ -12,6 +12,41 @@ found in the LICENSE file. #include +mitk::ROI::Element::Element() + : Properties(PropertyList::New()) +{ +} + +mitk::BaseProperty::ConstPointer mitk::ROI::Element::GetConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) const +{ + return Properties->GetConstProperty(propertyKey, contextName, fallBackOnDefaultContext); +} + +std::vector mitk::ROI::Element::GetPropertyKeys(const std::string& contextName, bool includeDefaultContext) const +{ + return Properties->GetPropertyKeys(contextName, includeDefaultContext); +} + +std::vector mitk::ROI::Element::GetPropertyContextNames() const +{ + return Properties->GetPropertyContextNames(); +} + +mitk::BaseProperty* mitk::ROI::Element::GetNonConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) +{ + return Properties->GetNonConstProperty(propertyKey, contextName, fallBackOnDefaultContext); +} + +void mitk::ROI::Element::SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName, bool fallBackOnDefaultContext) +{ + Properties->SetProperty(propertyKey, property, contextName, fallBackOnDefaultContext); +} + +void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) +{ + Properties->RemoveProperty(propertyKey, contextName, fallBackOnDefaultContext); +} + mitk::ROI::ROI() { } @@ -25,6 +60,47 @@ mitk::ROI::~ROI() { } +size_t mitk::ROI::GetNumberOfElements() const +{ + return m_Elements.size(); +} + +size_t mitk::ROI::AddElement(const Element& element) +{ + m_Elements.push_back(element); + return m_Elements.size() - 1; +} + +const mitk::ROI::Element* mitk::ROI::GetElement(size_t index) const +{ + return &m_Elements.at(index); +} + +mitk::ROI::Element* mitk::ROI::GetElement(size_t index) +{ + return &m_Elements.at(index); +} + +mitk::ROI::ConstIterator mitk::ROI::begin() const +{ + return m_Elements.begin(); +} + +mitk::ROI::ConstIterator mitk::ROI::end() const +{ + return m_Elements.end(); +} + +mitk::ROI::Iterator mitk::ROI::begin() +{ + return m_Elements.begin(); +} + +mitk::ROI::Iterator mitk::ROI::end() +{ + return m_Elements.end(); +} + void mitk::ROI::SetRequestedRegionToLargestPossibleRegion() { } From 2ceefbaab3089d2474508e7223f6c5adb0a5ebdf Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 27 Oct 2023 06:38:52 +0200 Subject: [PATCH 16/92] Read bounds and properties of ROIs --- Modules/ROI/autoload/IO/src/mitkROIIO.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp index 4be0e0d2b79..13bb4fe27cd 100644 --- a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -88,6 +88,14 @@ std::vector mitk::ROIIO::DoRead() for (const auto& jsonROI : json["ROIs"]) { + ROI::Element roi; + jsonROI["Min"].get_to(roi.Min); + jsonROI["Max"].get_to(roi.Max); + + if (jsonROI.contains("Properties")) + roi.Properties->FromJSON(jsonROI["Properties"]); + + result->AddElement(roi); } } catch (const nlohmann::json::exception &e) From 40081cb3270471085a7d1c3cdf8f4b74968bcc97 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 27 Oct 2023 06:39:21 +0200 Subject: [PATCH 17/92] Implement 3-d mapper for ROIs --- Modules/ROI/include/mitkROIMapper3D.h | 4 + Modules/ROI/src/mitkROIMapper3D.cpp | 104 ++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) diff --git a/Modules/ROI/include/mitkROIMapper3D.h b/Modules/ROI/include/mitkROIMapper3D.h index 91a8a65918b..a4bb8f8618a 100644 --- a/Modules/ROI/include/mitkROIMapper3D.h +++ b/Modules/ROI/include/mitkROIMapper3D.h @@ -31,6 +31,7 @@ namespace mitk ~LocalStorage() override; vtkPropAssembly* GetPropAssembly() const; + void SetPropAssembly(vtkPropAssembly* propAssembly); protected: vtkSmartPointer m_PropAssembly; @@ -48,6 +49,9 @@ namespace mitk ROIMapper3D(); ~ROIMapper3D() override; + void GenerateDataForRenderer(BaseRenderer* renderer) override; + void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override; + private: LocalStorageHandler m_LocalStorageHandler; }; diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index c3ff36625b6..5e802e2e82b 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -11,6 +11,44 @@ found in the LICENSE file. ============================================================================*/ #include +#include + +#include +#include +#include +#include + +namespace +{ + void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* properties, vtkActor* actor) + { + auto property = properties->GetConstProperty("color"); + + if (property.IsNotNull()) + { + auto colorProperty = dynamic_cast(property.GetPointer()); + + if (colorProperty != nullptr) + { + const auto color = colorProperty->GetColor(); + actor->GetProperty()->SetColor(color[0], color[1], color[2]); + } + } + + property = properties->GetConstProperty("opacity"); + + if (property.IsNotNull()) + { + auto opacityProperty = dynamic_cast(property.GetPointer()); + + if (opacityProperty != nullptr) + { + const auto opacity = opacityProperty->GetValue(); + actor->GetProperty()->SetOpacity(actor->GetProperty()->GetOpacity() * opacity); + } + } + } +} mitk::ROIMapper3D::LocalStorage::LocalStorage() : m_PropAssembly(vtkSmartPointer::New()) @@ -26,9 +64,15 @@ vtkPropAssembly* mitk::ROIMapper3D::LocalStorage::GetPropAssembly() const return m_PropAssembly; } +void mitk::ROIMapper3D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly) +{ + m_PropAssembly = propAssembly; +} + void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) { Superclass::SetDefaultProperties(node, renderer, override); + node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, override); } mitk::ROIMapper3D::ROIMapper3D() @@ -39,6 +83,66 @@ mitk::ROIMapper3D::~ROIMapper3D() { } +void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) +{ + const auto* dataNode = this->GetDataNode(); + + if (dataNode == nullptr) + return; + + auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); + + if (localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime()) + return; + + const auto* data = dynamic_cast(this->GetData()); + + if (data == nullptr) + return; + + const auto* geometry = data->GetGeometry(); + auto propAssembly = vtkSmartPointer::New(); + + if (dataNode->IsVisible(renderer)) + { + for (const auto& roi : *data) + { + Point3D min; + geometry->IndexToWorld(roi.Min, min); + + Point3D max; + geometry->IndexToWorld(roi.Max, max); + + auto cube = vtkSmartPointer::New(); + cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); + cube->Update(); + + auto mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(cube->GetOutputPort()); + + auto actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + this->ApplyColorAndOpacityProperties(renderer, actor); + ApplyIndividualColorAndOpacityProperties(roi.Properties, actor); + + propAssembly->AddPart(actor); + } + } + + localStorage->SetPropAssembly(propAssembly); + localStorage->UpdateGenerateDataTime(); +} + +void mitk::ROIMapper3D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) +{ + auto* property = actor->GetProperty(); + + float opacity = 1.0f; + this->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); +} + vtkProp* mitk::ROIMapper3D::GetVtkProp(BaseRenderer* renderer) { return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); From df878377fe3326a876152d38697e21d6e52bf36f Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 27 Oct 2023 06:48:38 +0200 Subject: [PATCH 18/92] Improve render style of ROIs in 3-d mapper --- Modules/ROI/src/mitkROIMapper3D.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index 5e802e2e82b..3c2a1beb592 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -126,6 +126,10 @@ void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) this->ApplyColorAndOpacityProperties(renderer, actor); ApplyIndividualColorAndOpacityProperties(roi.Properties, actor); + auto* property = actor->GetProperty(); + property->SetRepresentationToWireframe(); + property->LightingOff(); + propAssembly->AddPart(actor); } } From b22dbf4db75ffc46dfd738ff8c4424b5915a2bba Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 27 Oct 2023 07:05:16 +0200 Subject: [PATCH 19/92] Adjust bounds for ROI rendering to completely cover border pixels --- Modules/ROI/src/mitkROIMapper3D.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index 3c2a1beb592..73afe959c79 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -101,6 +101,8 @@ void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) return; const auto* geometry = data->GetGeometry(); + const auto halfSpacing = geometry->GetSpacing() * 0.5f; + auto propAssembly = vtkSmartPointer::New(); if (dataNode->IsVisible(renderer)) @@ -109,9 +111,11 @@ void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) { Point3D min; geometry->IndexToWorld(roi.Min, min); + min -= halfSpacing; Point3D max; geometry->IndexToWorld(roi.Max, max); + max += halfSpacing; auto cube = vtkSmartPointer::New(); cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); From 02a00c4ce1a6f89109bca5c6e676aa93b51cd4ee Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 27 Oct 2023 10:14:45 +0200 Subject: [PATCH 20/92] Implement 2-d mapper for ROIs --- Modules/ROI/include/mitkROIMapper2D.h | 8 ++ Modules/ROI/src/mitkROIMapper2D.cpp | 107 +++++++++++++++++++++++++- Modules/ROI/src/mitkROIMapper3D.cpp | 51 ++++++------ 3 files changed, 138 insertions(+), 28 deletions(-) diff --git a/Modules/ROI/include/mitkROIMapper2D.h b/Modules/ROI/include/mitkROIMapper2D.h index 430302e7619..f7c81f61e95 100644 --- a/Modules/ROI/include/mitkROIMapper2D.h +++ b/Modules/ROI/include/mitkROIMapper2D.h @@ -31,9 +31,14 @@ namespace mitk ~LocalStorage() override; vtkPropAssembly* GetPropAssembly() const; + void SetPropAssembly(vtkPropAssembly* propAssembly); + + const PlaneGeometry* GetLastPlaneGeometry() const; + void SetLastPlaneGeometry(const PlaneGeometry* planeGeometry); protected: vtkSmartPointer m_PropAssembly; + PlaneGeometry::ConstPointer m_LastPlaneGeometry; }; public: @@ -48,6 +53,9 @@ namespace mitk ROIMapper2D(); ~ROIMapper2D() override; + void GenerateDataForRenderer(BaseRenderer* renderer) override; + void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override; + private: LocalStorageHandler m_LocalStorageHandler; }; diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp index c2adedc3fc0..9caece5caf6 100644 --- a/Modules/ROI/src/mitkROIMapper2D.cpp +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -11,6 +11,17 @@ found in the LICENSE file. ============================================================================*/ #include +#include + +#include +#include +#include +#include +#include +#include + +// Implemented in mitkROIMapper3D.cpp +extern void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* properties, vtkActor* actor); mitk::ROIMapper2D::LocalStorage::LocalStorage() : m_PropAssembly(vtkSmartPointer::New()) @@ -26,9 +37,24 @@ vtkPropAssembly* mitk::ROIMapper2D::LocalStorage::GetPropAssembly() const return m_PropAssembly; } +void mitk::ROIMapper2D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly) +{ + m_PropAssembly = propAssembly; +} + +const mitk::PlaneGeometry* mitk::ROIMapper2D::LocalStorage::GetLastPlaneGeometry() const +{ + return m_LastPlaneGeometry; +} + +void mitk::ROIMapper2D::LocalStorage::SetLastPlaneGeometry(const PlaneGeometry* planeGeometry) +{ + m_LastPlaneGeometry = planeGeometry; +} + void mitk::ROIMapper2D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) { - Superclass::SetDefaultProperties(node, renderer, override); + // Implemented in ROIMapper3D } mitk::ROIMapper2D::ROIMapper2D() @@ -39,6 +65,85 @@ mitk::ROIMapper2D::~ROIMapper2D() { } +void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) +{ + const auto* dataNode = this->GetDataNode(); + + if (dataNode == nullptr) + return; + + auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); + const auto* planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); + + if (localStorage->GetLastPlaneGeometry() != nullptr && localStorage->GetLastPlaneGeometry()->IsOnPlane(planeGeometry) && + localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime()) + { + return; + } + + localStorage->SetLastPlaneGeometry(planeGeometry->Clone()); + + const auto* data = dynamic_cast(this->GetData()); + + if (data == nullptr) + return; + + auto propAssembly = vtkSmartPointer::New(); + + if (dataNode->IsVisible(renderer)) + { + const auto* geometry = data->GetGeometry(); + const auto halfSpacing = geometry->GetSpacing() * 0.5f; + + auto plane = vtkSmartPointer::New(); + plane->SetOrigin(planeGeometry->GetOrigin().data()); + plane->SetNormal(planeGeometry->GetNormal().data()); + + for (const auto& roi : *data) + { + Point3D min; + geometry->IndexToWorld(roi.Min, min); + min -= halfSpacing; + + Point3D max; + geometry->IndexToWorld(roi.Max, max); + max += halfSpacing; + + auto cube = vtkSmartPointer::New(); + cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); + + auto cutter = vtkSmartPointer::New(); + cutter->SetInputConnection(cube->GetOutputPort()); + cutter->SetCutFunction(plane); + cutter->Update(); + + auto mapper = vtkSmartPointer::New(); + mapper->SetInputConnection(cutter->GetOutputPort()); + mapper->SetResolveCoincidentTopologyToPolygonOffset(); + + auto actor = vtkSmartPointer::New(); + actor->SetMapper(mapper); + + this->ApplyColorAndOpacityProperties(renderer, actor); + ApplyIndividualColorAndOpacityProperties(roi.Properties, actor); + + propAssembly->AddPart(actor); + } + } + + localStorage->SetPropAssembly(propAssembly); + localStorage->UpdateGenerateDataTime(); +} + +void mitk::ROIMapper2D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) +{ + auto* property = actor->GetProperty(); + + float opacity = 1.0f; + this->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); +} + vtkProp* mitk::ROIMapper2D::GetVtkProp(BaseRenderer* renderer) { return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index 73afe959c79..a91446f479f 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -18,34 +18,35 @@ found in the LICENSE file. #include #include -namespace +// Used by both ROIMapper2D and ROIMapper3D +void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* properties, vtkActor* actor) { - void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* properties, vtkActor* actor) + actor->GetProperty()->SetRepresentationToWireframe(); + actor->GetProperty()->LightingOff(); + + auto property = properties->GetConstProperty("color"); + + if (property.IsNotNull()) { - auto property = properties->GetConstProperty("color"); + auto colorProperty = dynamic_cast(property.GetPointer()); - if (property.IsNotNull()) + if (colorProperty != nullptr) { - auto colorProperty = dynamic_cast(property.GetPointer()); - - if (colorProperty != nullptr) - { - const auto color = colorProperty->GetColor(); - actor->GetProperty()->SetColor(color[0], color[1], color[2]); - } + const auto color = colorProperty->GetColor(); + actor->GetProperty()->SetColor(color[0], color[1], color[2]); } + } - property = properties->GetConstProperty("opacity"); + property = properties->GetConstProperty("opacity"); - if (property.IsNotNull()) - { - auto opacityProperty = dynamic_cast(property.GetPointer()); + if (property.IsNotNull()) + { + auto opacityProperty = dynamic_cast(property.GetPointer()); - if (opacityProperty != nullptr) - { - const auto opacity = opacityProperty->GetValue(); - actor->GetProperty()->SetOpacity(actor->GetProperty()->GetOpacity() * opacity); - } + if (opacityProperty != nullptr) + { + const auto opacity = opacityProperty->GetValue(); + actor->GetProperty()->SetOpacity(actor->GetProperty()->GetOpacity() * opacity); } } } @@ -100,13 +101,13 @@ void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) if (data == nullptr) return; - const auto* geometry = data->GetGeometry(); - const auto halfSpacing = geometry->GetSpacing() * 0.5f; - auto propAssembly = vtkSmartPointer::New(); if (dataNode->IsVisible(renderer)) { + const auto* geometry = data->GetGeometry(); + const auto halfSpacing = geometry->GetSpacing() * 0.5f; + for (const auto& roi : *data) { Point3D min; @@ -130,10 +131,6 @@ void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) this->ApplyColorAndOpacityProperties(renderer, actor); ApplyIndividualColorAndOpacityProperties(roi.Properties, actor); - auto* property = actor->GetProperty(); - property->SetRepresentationToWireframe(); - property->LightingOff(); - propAssembly->AddPart(actor); } } From a1ca280d3eed33d32184276d88c942cba0531ebf Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Tue, 31 Oct 2023 16:23:38 +0100 Subject: [PATCH 21/92] Disable VTK info logs by default Some filters like vtkPolyDataPlaneCutter spam the terminal otherwise. --- Modules/AppUtil/src/mitkBaseApplication.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/AppUtil/src/mitkBaseApplication.cpp b/Modules/AppUtil/src/mitkBaseApplication.cpp index 68948a66073..91201a64145 100644 --- a/Modules/AppUtil/src/mitkBaseApplication.cpp +++ b/Modules/AppUtil/src/mitkBaseApplication.cpp @@ -29,6 +29,7 @@ found in the LICENSE file. #include +#include #include #include @@ -663,6 +664,8 @@ namespace mitk { if (nullptr == qApp) { + vtkLogger::SetStderrVerbosity(vtkLogger::VERBOSITY_WARNING); + vtkOpenGLRenderWindow::SetGlobalMaximumNumberOfMultiSamples(0); auto defaultFormat = QVTKOpenGLNativeWidget::defaultFormat(); From 6a2157f2d4b1e13166b630f1d06f2d71a9474222 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Tue, 31 Oct 2023 16:25:52 +0100 Subject: [PATCH 22/92] Support line width for ROIs Implemented as lineWidth FloatProperty. --- Modules/ROI/src/mitkROIMapper3D.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index a91446f479f..d16e746d801 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -19,7 +19,7 @@ found in the LICENSE file. #include // Used by both ROIMapper2D and ROIMapper3D -void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* properties, vtkActor* actor) +void ApplyIndividualProperties(const mitk::IPropertyProvider* properties, vtkActor* actor) { actor->GetProperty()->SetRepresentationToWireframe(); actor->GetProperty()->LightingOff(); @@ -49,6 +49,19 @@ void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* pro actor->GetProperty()->SetOpacity(actor->GetProperty()->GetOpacity() * opacity); } } + + property = properties->GetConstProperty("lineWidth"); + + if (property.IsNotNull()) + { + auto lineWidthProperty = dynamic_cast(property.GetPointer()); + + if (lineWidthProperty != nullptr) + { + const auto lineWidth = lineWidthProperty->GetValue(); + actor->GetProperty()->SetLineWidth(lineWidth); + } + } } mitk::ROIMapper3D::LocalStorage::LocalStorage() @@ -129,7 +142,7 @@ void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) actor->SetMapper(mapper); this->ApplyColorAndOpacityProperties(renderer, actor); - ApplyIndividualColorAndOpacityProperties(roi.Properties, actor); + ApplyIndividualProperties(roi.Properties, actor); propAssembly->AddPart(actor); } From b08e74d8089cc9913e17700b52d02a7529b4ddcd Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Tue, 31 Oct 2023 16:26:21 +0100 Subject: [PATCH 23/92] Use a more efficient plane cutter. --- Modules/ROI/src/mitkROIMapper2D.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp index 9caece5caf6..0b5ee14d77c 100644 --- a/Modules/ROI/src/mitkROIMapper2D.cpp +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -13,15 +13,14 @@ found in the LICENSE file. #include #include -#include #include -#include #include #include +#include #include // Implemented in mitkROIMapper3D.cpp -extern void ApplyIndividualColorAndOpacityProperties(const mitk::IPropertyProvider* properties, vtkActor* actor); +extern void ApplyIndividualProperties(const mitk::IPropertyProvider* properties, vtkActor* actor); mitk::ROIMapper2D::LocalStorage::LocalStorage() : m_PropAssembly(vtkSmartPointer::New()) @@ -112,20 +111,22 @@ void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) auto cube = vtkSmartPointer::New(); cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); - auto cutter = vtkSmartPointer::New(); + auto cutter = vtkSmartPointer::New(); cutter->SetInputConnection(cube->GetOutputPort()); - cutter->SetCutFunction(plane); + cutter->SetPlane(plane); cutter->Update(); + if (cutter->GetOutput()->GetNumberOfLines() == 0) + continue; + auto mapper = vtkSmartPointer::New(); mapper->SetInputConnection(cutter->GetOutputPort()); - mapper->SetResolveCoincidentTopologyToPolygonOffset(); auto actor = vtkSmartPointer::New(); actor->SetMapper(mapper); this->ApplyColorAndOpacityProperties(renderer, actor); - ApplyIndividualColorAndOpacityProperties(roi.Properties, actor); + ApplyIndividualProperties(roi.Properties, actor); propAssembly->AddPart(actor); } From ba321adff886e50b2fd2acb71479a8e992165e42 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 2 Nov 2023 13:28:58 +0100 Subject: [PATCH 24/92] Add base data properties to ROI file format --- Modules/ROI/autoload/IO/src/mitkROIIO.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp index 13bb4fe27cd..b992d658d17 100644 --- a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -86,6 +86,13 @@ std::vector mitk::ROIIO::DoRead() result->SetGeometry(ReadGeometry(json["Geometry"])); + if (json.contains("Properties")) + { + auto properties = mitk::PropertyList::New(); + properties->FromJSON(json["Properties"]); + result->GetPropertyList()->ConcatenatePropertyList(properties); + } + for (const auto& jsonROI : json["ROIs"]) { ROI::Element roi; From 47c09dde6360dc31573568ae405d85252639d208 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 2 Nov 2023 13:29:15 +0100 Subject: [PATCH 25/92] Add default font properties --- Modules/ROI/src/mitkROIMapper3D.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index d16e746d801..605eb7aafe7 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -86,7 +86,11 @@ void mitk::ROIMapper3D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssem void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) { Superclass::SetDefaultProperties(node, renderer, override); + node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, override); + node->AddProperty("font.bold", mitk::BoolProperty::New(false), renderer, override); + node->AddProperty("font.italic", mitk::BoolProperty::New(false), renderer, override); + node->AddProperty("font.size", mitk::IntProperty::New(16), renderer, override); } mitk::ROIMapper3D::ROIMapper3D() From f1a97bd5334709178e547c2b4f5aad659d9c8998 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 2 Nov 2023 13:29:53 +0100 Subject: [PATCH 26/92] Implement captions/names for ROIs --- Modules/ROI/CMakeLists.txt | 1 + Modules/ROI/include/mitkROIMapper2D.h | 9 ++- Modules/ROI/src/mitkROIMapper2D.cpp | 91 ++++++++++++++++++++++++++- 3 files changed, 98 insertions(+), 3 deletions(-) diff --git a/Modules/ROI/CMakeLists.txt b/Modules/ROI/CMakeLists.txt index 147dcf0fcfc..e4ab9d0abed 100644 --- a/Modules/ROI/CMakeLists.txt +++ b/Modules/ROI/CMakeLists.txt @@ -1,5 +1,6 @@ mitk_create_module( DEPENDS MitkCore + PACKAGE_DEPENDS VTK|RenderingAnnotation ) add_subdirectory(autoload/IO) diff --git a/Modules/ROI/include/mitkROIMapper2D.h b/Modules/ROI/include/mitkROIMapper2D.h index f7c81f61e95..b7443b3c2fd 100644 --- a/Modules/ROI/include/mitkROIMapper2D.h +++ b/Modules/ROI/include/mitkROIMapper2D.h @@ -17,8 +17,11 @@ found in the LICENSE file. #include #include -#include -#include +template +class vtkSmartPointer; + +class vtkPropAssembly; +class vtkCaptionActor2D; namespace mitk { @@ -57,6 +60,8 @@ namespace mitk void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override; private: + vtkSmartPointer CreateCaptionActor(const std::string& caption, double* attachmentPoint, vtkProperty* property, const BaseRenderer* renderer) const; + LocalStorageHandler m_LocalStorageHandler; }; } diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp index 0b5ee14d77c..562c7369171 100644 --- a/Modules/ROI/src/mitkROIMapper2D.cpp +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -13,15 +13,60 @@ found in the LICENSE file. #include #include +#include #include #include #include #include +#include #include +#include +#include // Implemented in mitkROIMapper3D.cpp extern void ApplyIndividualProperties(const mitk::IPropertyProvider* properties, vtkActor* actor); +namespace +{ + std::string GetName(const mitk::ROI::Element& roi) + { + auto property = roi.GetConstProperty("name"); + + if (property.IsNotNull()) + { + auto nameProperty = dynamic_cast(property.GetPointer()); + + if (nameProperty != nullptr) + return nameProperty->GetValue(); + } + + return ""; + } + + mitk::Point3D GetBottomLeftPoint(vtkPoints* points, mitk::BaseRenderer* renderer) + { + mitk::Point3D point = points->GetPoint(0); + mitk::Point2D bottomLeftDisplayPoint; + renderer->WorldToDisplay(point, bottomLeftDisplayPoint); + + auto numPoints = points->GetNumberOfPoints(); + mitk::Point2D displayPoint; + + for (decltype(numPoints) i = 1; i < numPoints; ++i) + { + point.FillPoint(points->GetPoint(i)); + renderer->WorldToDisplay(point, displayPoint); + + bottomLeftDisplayPoint[0] = std::min(bottomLeftDisplayPoint[0], displayPoint[0]); + bottomLeftDisplayPoint[1] = std::min(bottomLeftDisplayPoint[1], displayPoint[1]); + } + + renderer->DisplayToWorld(bottomLeftDisplayPoint, point); + + return point; + } +} + mitk::ROIMapper2D::LocalStorage::LocalStorage() : m_PropAssembly(vtkSmartPointer::New()) { @@ -116,7 +161,9 @@ void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) cutter->SetPlane(plane); cutter->Update(); - if (cutter->GetOutput()->GetNumberOfLines() == 0) + auto* slicePolyData = cutter->GetOutput(); + + if (slicePolyData->GetNumberOfLines() == 0) continue; auto mapper = vtkSmartPointer::New(); @@ -129,6 +176,16 @@ void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) ApplyIndividualProperties(roi.Properties, actor); propAssembly->AddPart(actor); + + auto name = GetName(roi); + + if (!name.empty()) + { + auto bottomLeftPoint = GetBottomLeftPoint(slicePolyData->GetPoints(), renderer); + auto captionActor = CreateCaptionActor(name, bottomLeftPoint.data(), actor->GetProperty(), renderer); + + propAssembly->AddPart(captionActor); + } } } @@ -149,3 +206,35 @@ vtkProp* mitk::ROIMapper2D::GetVtkProp(BaseRenderer* renderer) { return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); } + +vtkSmartPointer mitk::ROIMapper2D::CreateCaptionActor(const std::string& caption, double* attachmentPoint, vtkProperty* property, const BaseRenderer* renderer) const +{ + auto captionActor = vtkSmartPointer::New(); + captionActor->SetPosition(property->GetLineWidth() * 0.5, property->GetLineWidth() * 0.5); + captionActor->GetTextActor()->SetTextScaleModeToNone(); + captionActor->SetAttachmentPoint(attachmentPoint); + captionActor->SetCaption(caption.c_str()); + captionActor->BorderOff(); + captionActor->LeaderOff(); + + auto* textProperty = captionActor->GetCaptionTextProperty(); + textProperty->SetColor(property->GetColor()); + textProperty->SetOpacity(property->GetOpacity()); + textProperty->ShadowOff(); + + auto* dataNode = this->GetDataNode(); + + int fontSize = 16; + dataNode->GetIntProperty("font.size", fontSize, renderer); + textProperty->SetFontSize(fontSize); + + bool bold = false; + dataNode->GetBoolProperty("font.bold", bold, renderer); + textProperty->SetBold(bold); + + bool italic = false; + dataNode->GetBoolProperty("font.italic", italic, renderer); + textProperty->SetItalic(italic); + + return captionActor; +} From 619aba23ea36df82d61f134d868a5fd51bdca734 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 3 Nov 2023 17:12:54 +0100 Subject: [PATCH 27/92] Require IDs for ROIs --- Modules/ROI/autoload/IO/src/mitkROIIO.cpp | 1 + Modules/ROI/include/mitkROI.h | 1 + 2 files changed, 2 insertions(+) diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp index b992d658d17..d4adec71803 100644 --- a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -96,6 +96,7 @@ std::vector mitk::ROIIO::DoRead() for (const auto& jsonROI : json["ROIs"]) { ROI::Element roi; + jsonROI["ID"].get_to(roi.ID); jsonROI["Min"].get_to(roi.Min); jsonROI["Max"].get_to(roi.Max); diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h index fb5851e1d56..92e60cc288a 100644 --- a/Modules/ROI/include/mitkROI.h +++ b/Modules/ROI/include/mitkROI.h @@ -34,6 +34,7 @@ namespace mitk void SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; + unsigned int ID; Point3D Min; Point3D Max; PropertyList::Pointer Properties; From 62b66c55c4c045bbebe7ec8f9b4b1687a130dfc7 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 3 Nov 2023 17:17:53 +0100 Subject: [PATCH 28/92] Implement ROI caption parsing Captions can now have placeholders in curly braces which will get substituted with corresponding data or property values at runtime. Also refactored some ROI mapper code into a common ROIMapperHelper namespace. --- Modules/ROI/files.cmake | 2 + Modules/ROI/include/mitkROIMapper2D.h | 3 - Modules/ROI/src/mitkROIMapper2D.cpp | 64 ++++-------- Modules/ROI/src/mitkROIMapper3D.cpp | 56 +--------- Modules/ROI/src/mitkROIMapperHelper.cpp | 130 ++++++++++++++++++++++++ Modules/ROI/src/mitkROIMapperHelper.h | 48 +++++++++ 6 files changed, 201 insertions(+), 102 deletions(-) create mode 100644 Modules/ROI/src/mitkROIMapperHelper.cpp create mode 100644 Modules/ROI/src/mitkROIMapperHelper.h diff --git a/Modules/ROI/files.cmake b/Modules/ROI/files.cmake index 8526fe310ae..9880797ab82 100644 --- a/Modules/ROI/files.cmake +++ b/Modules/ROI/files.cmake @@ -2,6 +2,7 @@ set(H_FILES include/mitkROI.h include/mitkROIMapper2D.h include/mitkROIMapper3D.h + src/mitkROIMapperHelper.h src/mitkROIObjectFactory.h ) @@ -9,5 +10,6 @@ set(CPP_FILES mitkROI.cpp mitkROIMapper2D.cpp mitkROIMapper3D.cpp + mitkROIMapperHelper.cpp mitkROIObjectFactory.cpp ) diff --git a/Modules/ROI/include/mitkROIMapper2D.h b/Modules/ROI/include/mitkROIMapper2D.h index b7443b3c2fd..0e8323592dd 100644 --- a/Modules/ROI/include/mitkROIMapper2D.h +++ b/Modules/ROI/include/mitkROIMapper2D.h @@ -21,7 +21,6 @@ template class vtkSmartPointer; class vtkPropAssembly; -class vtkCaptionActor2D; namespace mitk { @@ -60,8 +59,6 @@ namespace mitk void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override; private: - vtkSmartPointer CreateCaptionActor(const std::string& caption, double* attachmentPoint, vtkProperty* property, const BaseRenderer* renderer) const; - LocalStorageHandler m_LocalStorageHandler; }; } diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp index 562c7369171..c7784d6969f 100644 --- a/Modules/ROI/src/mitkROIMapper2D.cpp +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -11,20 +11,18 @@ found in the LICENSE file. ============================================================================*/ #include -#include -#include +#include "mitkROIMapperHelper.h" + #include #include #include #include #include -#include -#include -#include -// Implemented in mitkROIMapper3D.cpp -extern void ApplyIndividualProperties(const mitk::IPropertyProvider* properties, vtkActor* actor); +#include + +#include namespace { @@ -98,7 +96,8 @@ void mitk::ROIMapper2D::LocalStorage::SetLastPlaneGeometry(const PlaneGeometry* void mitk::ROIMapper2D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) { - // Implemented in ROIMapper3D + Superclass::SetDefaultProperties(node, renderer, override); + ROIMapperHelper::SetDefaultProperties(node, renderer, override); } mitk::ROIMapper2D::ROIMapper2D() @@ -173,18 +172,21 @@ void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) actor->SetMapper(mapper); this->ApplyColorAndOpacityProperties(renderer, actor); - ApplyIndividualProperties(roi.Properties, actor); + ROIMapperHelper::ApplyIndividualProperties(roi.Properties, actor); propAssembly->AddPart(actor); - auto name = GetName(roi); - - if (!name.empty()) + if (std::string caption; dataNode->GetStringProperty("caption", caption, renderer)) { - auto bottomLeftPoint = GetBottomLeftPoint(slicePolyData->GetPoints(), renderer); - auto captionActor = CreateCaptionActor(name, bottomLeftPoint.data(), actor->GetProperty(), renderer); + caption = ROIMapperHelper::ParseCaption(caption, roi); + + if (!caption.empty()) + { + auto bottomLeftPoint = GetBottomLeftPoint(slicePolyData->GetPoints(), renderer); + auto captionActor = ROIMapperHelper::CreateCaptionActor(caption, bottomLeftPoint, actor->GetProperty(), dataNode, renderer); - propAssembly->AddPart(captionActor); + propAssembly->AddPart(captionActor); + } } } } @@ -206,35 +208,3 @@ vtkProp* mitk::ROIMapper2D::GetVtkProp(BaseRenderer* renderer) { return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); } - -vtkSmartPointer mitk::ROIMapper2D::CreateCaptionActor(const std::string& caption, double* attachmentPoint, vtkProperty* property, const BaseRenderer* renderer) const -{ - auto captionActor = vtkSmartPointer::New(); - captionActor->SetPosition(property->GetLineWidth() * 0.5, property->GetLineWidth() * 0.5); - captionActor->GetTextActor()->SetTextScaleModeToNone(); - captionActor->SetAttachmentPoint(attachmentPoint); - captionActor->SetCaption(caption.c_str()); - captionActor->BorderOff(); - captionActor->LeaderOff(); - - auto* textProperty = captionActor->GetCaptionTextProperty(); - textProperty->SetColor(property->GetColor()); - textProperty->SetOpacity(property->GetOpacity()); - textProperty->ShadowOff(); - - auto* dataNode = this->GetDataNode(); - - int fontSize = 16; - dataNode->GetIntProperty("font.size", fontSize, renderer); - textProperty->SetFontSize(fontSize); - - bool bold = false; - dataNode->GetBoolProperty("font.bold", bold, renderer); - textProperty->SetBold(bold); - - bool italic = false; - dataNode->GetBoolProperty("font.italic", italic, renderer); - textProperty->SetItalic(italic); - - return captionActor; -} diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index 605eb7aafe7..f68813ac34e 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -13,57 +13,13 @@ found in the LICENSE file. #include #include +#include "mitkROIMapperHelper.h" + #include #include #include #include -// Used by both ROIMapper2D and ROIMapper3D -void ApplyIndividualProperties(const mitk::IPropertyProvider* properties, vtkActor* actor) -{ - actor->GetProperty()->SetRepresentationToWireframe(); - actor->GetProperty()->LightingOff(); - - auto property = properties->GetConstProperty("color"); - - if (property.IsNotNull()) - { - auto colorProperty = dynamic_cast(property.GetPointer()); - - if (colorProperty != nullptr) - { - const auto color = colorProperty->GetColor(); - actor->GetProperty()->SetColor(color[0], color[1], color[2]); - } - } - - property = properties->GetConstProperty("opacity"); - - if (property.IsNotNull()) - { - auto opacityProperty = dynamic_cast(property.GetPointer()); - - if (opacityProperty != nullptr) - { - const auto opacity = opacityProperty->GetValue(); - actor->GetProperty()->SetOpacity(actor->GetProperty()->GetOpacity() * opacity); - } - } - - property = properties->GetConstProperty("lineWidth"); - - if (property.IsNotNull()) - { - auto lineWidthProperty = dynamic_cast(property.GetPointer()); - - if (lineWidthProperty != nullptr) - { - const auto lineWidth = lineWidthProperty->GetValue(); - actor->GetProperty()->SetLineWidth(lineWidth); - } - } -} - mitk::ROIMapper3D::LocalStorage::LocalStorage() : m_PropAssembly(vtkSmartPointer::New()) { @@ -86,11 +42,7 @@ void mitk::ROIMapper3D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssem void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) { Superclass::SetDefaultProperties(node, renderer, override); - - node->AddProperty("opacity", mitk::FloatProperty::New(1.0f), renderer, override); - node->AddProperty("font.bold", mitk::BoolProperty::New(false), renderer, override); - node->AddProperty("font.italic", mitk::BoolProperty::New(false), renderer, override); - node->AddProperty("font.size", mitk::IntProperty::New(16), renderer, override); + ROIMapperHelper::SetDefaultProperties(node, renderer, override); } mitk::ROIMapper3D::ROIMapper3D() @@ -146,7 +98,7 @@ void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) actor->SetMapper(mapper); this->ApplyColorAndOpacityProperties(renderer, actor); - ApplyIndividualProperties(roi.Properties, actor); + ROIMapperHelper::ApplyIndividualProperties(roi.Properties, actor); propAssembly->AddPart(actor); } diff --git a/Modules/ROI/src/mitkROIMapperHelper.cpp b/Modules/ROI/src/mitkROIMapperHelper.cpp new file mode 100644 index 00000000000..cbf5b99bff5 --- /dev/null +++ b/Modules/ROI/src/mitkROIMapperHelper.cpp @@ -0,0 +1,130 @@ +/*============================================================================ + +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 "mitkROIMapperHelper.h" + +#include +#include + +#include + +#include + +void mitk::ROIMapperHelper::ApplyIndividualProperties(const IPropertyProvider* properties, vtkActor* actor) +{ + auto* property = actor->GetProperty(); + + property->SetRepresentationToWireframe(); + property->LightingOff(); + + if (auto colorProperty = GetConstProperty("color", properties); colorProperty != nullptr) + { + const auto color = colorProperty->GetColor(); + property->SetColor(color[0], color[1], color[2]); + } + + if (auto opacityProperty = GetConstProperty("opacity", properties); opacityProperty != nullptr) + { + const auto opacity = opacityProperty->GetValue(); + property->SetOpacity(property->GetOpacity() * opacity); + } + + if (auto lineWidthProperty = GetConstProperty("lineWidth", properties); lineWidthProperty != nullptr) + { + const auto lineWidth = lineWidthProperty->GetValue(); + property->SetLineWidth(lineWidth); + } +} + +vtkSmartPointer mitk::ROIMapperHelper::CreateCaptionActor(const std::string& caption, const Point3D& attachmentPoint, vtkProperty* property, const DataNode* dataNode, const BaseRenderer* renderer) +{ + auto captionActor = vtkSmartPointer::New(); + captionActor->SetPosition(property->GetLineWidth() * 0.5, property->GetLineWidth() * 0.5); + captionActor->GetTextActor()->SetTextScaleModeToNone(); + captionActor->SetAttachmentPoint(attachmentPoint[0], attachmentPoint[1], attachmentPoint[2]); + captionActor->SetCaption(caption.c_str()); + captionActor->BorderOff(); + captionActor->LeaderOff(); + + auto* textProperty = captionActor->GetCaptionTextProperty(); + textProperty->SetColor(property->GetColor()); + textProperty->SetOpacity(property->GetOpacity()); + textProperty->ShadowOff(); + + int fontSize = 16; + dataNode->GetIntProperty("font.size", fontSize, renderer); + textProperty->SetFontSize(fontSize); + + bool bold = false; + dataNode->GetBoolProperty("font.bold", bold, renderer); + textProperty->SetBold(bold); + + bool italic = false; + dataNode->GetBoolProperty("font.italic", italic, renderer); + textProperty->SetItalic(italic); + + return captionActor; +} + +std::string mitk::ROIMapperHelper::ParseCaption(const std::string& captionTemplate, const ROI::Element& roi) +{ + std::regex regex(R"(\{([^}]*)\})"); // Anything between curly braces (considered as placeholder). + + auto start = captionTemplate.cbegin(); + bool hasPlaceholders = false; + std::string caption; + std::smatch match; + + // Iterate through the caption template and substitute all + // placeholders with corresponding data or property values. + + while (std::regex_search(start, captionTemplate.cend(), match, regex)) + { + hasPlaceholders = true; + + caption.append(match.prefix().first, match.prefix().second); + + if (match[1] == "ID") + { + caption.append(std::to_string(roi.ID)); + } + else + { + auto property = roi.GetConstProperty(match[1]); + + if (property.IsNotNull()) + caption.append(property->GetValueAsString()); + } + + start = match.suffix().first; + } + + if (match.suffix().matched) + caption.append(match.suffix().first, match.suffix().second); + + if (hasPlaceholders) + { + boost::trim(caption); + return caption; + } + + return captionTemplate; +} + +void mitk::ROIMapperHelper::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) +{ + node->AddProperty("opacity", FloatProperty::New(1.0f), renderer, override); + node->AddProperty("font.bold", BoolProperty::New(false), renderer, override); + node->AddProperty("font.italic", BoolProperty::New(false), renderer, override); + node->AddProperty("font.size", IntProperty::New(16), renderer, override); + node->AddProperty("caption", StringProperty::New("{ID}\n{name}"), renderer, override); +} diff --git a/Modules/ROI/src/mitkROIMapperHelper.h b/Modules/ROI/src/mitkROIMapperHelper.h new file mode 100644 index 00000000000..48161c27adb --- /dev/null +++ b/Modules/ROI/src/mitkROIMapperHelper.h @@ -0,0 +1,48 @@ +/*============================================================================ + +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 mitkROIMapperHelper_h +#define mitkROIMapperHelper_h + +#include +#include + +#include +#include +#include + +namespace mitk +{ + namespace ROIMapperHelper + { + void ApplyIndividualProperties(const IPropertyProvider* properties, vtkActor* actor); + + vtkSmartPointer CreateCaptionActor(const std::string& caption, const Point3D& attachmentPoint, vtkProperty* property, const DataNode* dataNode, const BaseRenderer* renderer); + + std::string ParseCaption(const std::string& captionTemplate, const ROI::Element& roi); + + void SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override); + + template + const T* GetConstProperty(const std::string& propertyKey, const mitk::IPropertyProvider* properties) + { + auto property = properties->GetConstProperty(propertyKey); + + if (property.IsNotNull()) + return dynamic_cast(property.GetPointer()); + + return nullptr; + } + } +} + +#endif From 4cd372476c1454b283ce0f450963e8ceb2df460c Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 12:50:23 +0100 Subject: [PATCH 29/92] Make ROI elements time-resolved Elements have a default property list. Each time step optionally has a more specific property list, with the default property list as fallback. Since the context type is restricted to be a string rather than a TimeStepType, use std::to_string() and std::stoul() to convert between these types. --- Modules/ROI/include/mitkROI.h | 42 ++++++-- Modules/ROI/src/mitkROI.cpp | 190 +++++++++++++++++++++++++++++++--- 2 files changed, 209 insertions(+), 23 deletions(-) diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h index 92e60cc288a..0e76d25c220 100644 --- a/Modules/ROI/include/mitkROI.h +++ b/Modules/ROI/include/mitkROI.h @@ -21,9 +21,14 @@ namespace mitk class MITKROI_EXPORT ROI : public BaseData { public: - struct MITKROI_EXPORT Element : IPropertyOwner + class MITKROI_EXPORT Element : public IPropertyOwner { + public: + using PointsType = std::map; + using PropertyListsType = std::map; + Element(); + explicit Element(unsigned int id); ~Element() = default; BaseProperty::ConstPointer GetConstProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = true) const override; @@ -34,25 +39,44 @@ namespace mitk void SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; - unsigned int ID; - Point3D Min; - Point3D Max; - PropertyList::Pointer Properties; + unsigned int GetID() const; + void SetID(unsigned int id); + + bool HasTimeStep(TimeStepType t) const; + + Point3D GetMin(TimeStepType t = 0) const; + void SetMin(const Point3D& min, TimeStepType t = 0); + + Point3D GetMax(TimeStepType t = 0) const; + void SetMax(const Point3D& max, TimeStepType t = 0); + + PropertyList* GetDefaultProperties() const; + void SetDefaultProperties(PropertyList* properties); + + PropertyList* GetProperties(TimeStepType t = 0) const; + void SetProperties(PropertyList* properties, TimeStepType t = 0); + + private: + unsigned int m_ID; + PointsType m_Min; + PointsType m_Max; + PropertyList::Pointer m_DefaultProperties; + PropertyListsType m_Properties; }; mitkClassMacro(ROI, BaseData) itkFactorylessNewMacro(Self) itkCloneMacro(Self) - using ElementsType = std::vector; + using ElementsType = std::map; using Iterator = ElementsType::iterator; using ConstIterator = ElementsType::const_iterator; size_t GetNumberOfElements() const; - size_t AddElement(const Element& element); + void AddElement(const Element& element); - const Element* GetElement(size_t index) const; - Element* GetElement(size_t index); + const Element& GetElement(unsigned int id) const; + Element& GetElement(unsigned int id); ConstIterator begin() const; ConstIterator end() const; diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp index 67f0540aa8f..7243726046b 100644 --- a/Modules/ROI/src/mitkROI.cpp +++ b/Modules/ROI/src/mitkROI.cpp @@ -13,38 +13,196 @@ found in the LICENSE file. #include mitk::ROI::Element::Element() - : Properties(PropertyList::New()) + : Element(0) { } +mitk::ROI::Element::Element(unsigned int id) + : m_ID(id), + m_DefaultProperties(PropertyList::New()) +{ +} + +unsigned int mitk::ROI::Element::GetID() const +{ + return m_ID; +} + +void mitk::ROI::Element::SetID(unsigned int id) +{ + m_ID = id; +} + +bool mitk::ROI::Element::HasTimeStep(TimeStepType t) const +{ + return m_Min.count(t) != 0 && m_Max.count(t) != 0; +} + +mitk::Point3D mitk::ROI::Element::GetMin(TimeStepType t) const +{ + return m_Min.at(t); +} + +void mitk::ROI::Element::SetMin(const Point3D& min, TimeStepType t) +{ + m_Min[t] = min; +} + +mitk::Point3D mitk::ROI::Element::GetMax(TimeStepType t) const +{ + return m_Max.at(t); +} + +void mitk::ROI::Element::SetMax(const Point3D& max, TimeStepType t) +{ + m_Max[t] = max; +} + +mitk::PropertyList* mitk::ROI::Element::GetDefaultProperties() const +{ + return m_DefaultProperties; +} + +void mitk::ROI::Element::SetDefaultProperties(PropertyList* properties) +{ + m_DefaultProperties = properties; +} + +mitk::PropertyList* mitk::ROI::Element::GetProperties(TimeStepType t) const +{ + if (m_Properties.count(t) != 0) + return m_Properties.at(t); + + return nullptr; +} + +void mitk::ROI::Element::SetProperties(PropertyList* properties, TimeStepType t) +{ + m_Properties[t] = properties; +} + mitk::BaseProperty::ConstPointer mitk::ROI::Element::GetConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) const { - return Properties->GetConstProperty(propertyKey, contextName, fallBackOnDefaultContext); + if (!contextName.empty()) + { + const TimeStepType t = std::stoul(contextName); + auto it = m_Properties.find(t); + + if (it != m_Properties.end() && it->second.IsNotNull()) + { + auto property = it->second->GetConstProperty(propertyKey); + + if (property.IsNotNull()) + return property; + } + + if (!fallBackOnDefaultContext) + return nullptr; + } + + return m_DefaultProperties->GetConstProperty(propertyKey); } std::vector mitk::ROI::Element::GetPropertyKeys(const std::string& contextName, bool includeDefaultContext) const { - return Properties->GetPropertyKeys(contextName, includeDefaultContext); + if (!contextName.empty()) + { + const TimeStepType t = std::stoul(contextName); + auto it = m_Properties.find(t); + + std::vector result; + + if (it != m_Properties.end() && it->second.IsNotNull()) + result = it->second->GetPropertyKeys(); + + if (includeDefaultContext) + { + auto keys = m_DefaultProperties->GetPropertyKeys(); + auto end = result.cend(); + + std::remove_copy_if(keys.cbegin(), keys.cend(), std::back_inserter(result), [&, result, end](const std::string& key) { + return end != std::find(result.cbegin(), end, key); + }); + } + + return result; + } + + return m_DefaultProperties->GetPropertyKeys(); } std::vector mitk::ROI::Element::GetPropertyContextNames() const { - return Properties->GetPropertyContextNames(); + std::vector result; + result.reserve(m_Properties.size()); + + std::transform(m_Properties.cbegin(), m_Properties.cend(), std::back_inserter(result), [](const PropertyListsType::value_type& property) { + return std::to_string(property.first); + }); + + return result; } mitk::BaseProperty* mitk::ROI::Element::GetNonConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) { - return Properties->GetNonConstProperty(propertyKey, contextName, fallBackOnDefaultContext); + if (!contextName.empty()) + { + const TimeStepType t = std::stoul(contextName); + auto it = m_Properties.find(t); + + if (it != m_Properties.end() && it->second.IsNotNull()) + { + auto* property = it->second->GetNonConstProperty(propertyKey); + + if (property != nullptr) + return property; + } + + if (!fallBackOnDefaultContext) + return nullptr; + } + + return m_DefaultProperties->GetNonConstProperty(propertyKey); } void mitk::ROI::Element::SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName, bool fallBackOnDefaultContext) { - Properties->SetProperty(propertyKey, property, contextName, fallBackOnDefaultContext); + if (!contextName.empty()) + { + const TimeStepType t = std::stoul(contextName); + auto it = m_Properties.find(t); + + if (it != m_Properties.end() && it->second.IsNotNull()) + { + it->second->SetProperty(propertyKey, property); + } + else if (!fallBackOnDefaultContext) + { + mitkThrow() << "Context \"" << contextName << "\" does not exist!"; + } + } + + m_DefaultProperties->SetProperty(propertyKey, property); } void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) { - Properties->RemoveProperty(propertyKey, contextName, fallBackOnDefaultContext); + if (!contextName.empty()) + { + const TimeStepType t = std::stoul(contextName); + auto it = m_Properties.find(t); + + if (it != m_Properties.end() && it->second.IsNotNull()) + { + it->second->RemoveProperty(propertyKey); + } + else if (!fallBackOnDefaultContext) + { + mitkThrow() << "Context \"" << contextName << "\" does not exist!"; + } + } + + m_DefaultProperties->RemoveProperty(propertyKey); } mitk::ROI::ROI() @@ -65,20 +223,24 @@ size_t mitk::ROI::GetNumberOfElements() const return m_Elements.size(); } -size_t mitk::ROI::AddElement(const Element& element) +void mitk::ROI::AddElement(const Element& element) { - m_Elements.push_back(element); - return m_Elements.size() - 1; + const auto id = element.GetID(); + + if (m_Elements.count(id) != 0) + mitkThrow() << "ROI already contains an element with ID " << std::to_string(id) << '.'; + + m_Elements[id] = element; } -const mitk::ROI::Element* mitk::ROI::GetElement(size_t index) const +const mitk::ROI::Element& mitk::ROI::GetElement(unsigned int id) const { - return &m_Elements.at(index); + return m_Elements.at(id); } -mitk::ROI::Element* mitk::ROI::GetElement(size_t index) +mitk::ROI::Element& mitk::ROI::GetElement(unsigned int id) { - return &m_Elements.at(index); + return m_Elements.at(id); } mitk::ROI::ConstIterator mitk::ROI::begin() const From c2c5571974e7b29fbdd7acc72233527b14eddfa6 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 12:58:24 +0100 Subject: [PATCH 30/92] Support time steps in MITK ROI file format. The "Geometry" object has an optional "TimeSteps" value specifying the number of time steps. The "ROI" object has an optional "TimeSteps" array, which, when present, contains objects consisting of a mandatory time step number "t", "Min" and "Max" points, as well as an optional "Properties" object. "ROI" objects with a "TimeSteps" array are not supposed to contain "Min" and "Max" points directly anymore. Their "Properties" object still act as a fallback for the optional "Properties" objects of the time steps, though. --- Modules/ROI/autoload/IO/src/mitkROIIO.cpp | 56 ++++++++++++++++++----- 1 file changed, 44 insertions(+), 12 deletions(-) diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp index d4adec71803..31a8e2c1eef 100644 --- a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -11,6 +11,7 @@ found in the LICENSE file. ============================================================================*/ #include "mitkROIIO.h" +#include #include #include @@ -21,27 +22,34 @@ found in the LICENSE file. namespace { - mitk::Geometry3D::Pointer ReadGeometry(const nlohmann::json& jsonGeometry) + mitk::TimeGeometry::Pointer ReadGeometry(const nlohmann::json& jsonGeometry) { - auto result = mitk::Geometry3D::New(); - result->ImageGeometryOn(); + auto geometry = mitk::Geometry3D::New(); + geometry->ImageGeometryOn(); if (!jsonGeometry.is_object()) mitkThrow() << "Geometry is expected to be a JSON object."; if (jsonGeometry.contains("Origin")) - result->SetOrigin(jsonGeometry["Origin"].get()); + geometry->SetOrigin(jsonGeometry["Origin"].get()); if (jsonGeometry.contains("Spacing")) - result->SetSpacing(jsonGeometry["Spacing"].get()); + geometry->SetSpacing(jsonGeometry["Spacing"].get()); if (jsonGeometry.contains("Size")) { auto size = jsonGeometry["Size"].get(); mitk::BaseGeometry::BoundsArrayType bounds({ 0.0, size[0], 0.0, size[1], 0.0, size[2] }); - result->SetBounds(bounds); + geometry->SetBounds(bounds); } + auto timeSteps = jsonGeometry.contains("TimeSteps") + ? jsonGeometry["TimeSteps"].get() + : 1; + + auto result = mitk::ProportionalTimeGeometry::New(); + result->Initialize(geometry, timeSteps); + return result; } } @@ -84,7 +92,7 @@ std::vector mitk::ROIIO::DoRead() if (1 != json["Version"].get()) mitkThrow() << "Unknown file format version (expected version 1)!"; - result->SetGeometry(ReadGeometry(json["Geometry"])); + result->SetTimeGeometry(ReadGeometry(json["Geometry"])); if (json.contains("Properties")) { @@ -95,13 +103,37 @@ std::vector mitk::ROIIO::DoRead() for (const auto& jsonROI : json["ROIs"]) { - ROI::Element roi; - jsonROI["ID"].get_to(roi.ID); - jsonROI["Min"].get_to(roi.Min); - jsonROI["Max"].get_to(roi.Max); + ROI::Element roi(jsonROI["ID"].get()); + + if (jsonROI.contains("TimeSteps")) + { + for (const auto& jsonTimeStep : jsonROI["TimeSteps"]) + { + auto t = jsonTimeStep["t"].get(); + + roi.SetMin(jsonTimeStep["Min"].get(), t); + roi.SetMax(jsonTimeStep["Max"].get(), t); + + if (jsonTimeStep.contains("Properties")) + { + auto properties = mitk::PropertyList::New(); + properties->FromJSON(jsonTimeStep["Properties"]); + roi.SetProperties(properties, t); + } + } + } + else + { + roi.SetMin(jsonROI["Min"].get()); + roi.SetMax(jsonROI["Max"].get()); + } if (jsonROI.contains("Properties")) - roi.Properties->FromJSON(jsonROI["Properties"]); + { + auto properties = mitk::PropertyList::New(); + properties->FromJSON(jsonROI["Properties"]); + roi.SetDefaultProperties(properties); + } result->AddElement(roi); } From fba71b9a6c540a3d8ec8e44040e2f798446ae903 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 13:00:53 +0100 Subject: [PATCH 31/92] Refactor common local storage parts of ROI mappers into base class --- Modules/ROI/files.cmake | 2 + Modules/ROI/include/mitkROIMapper2D.h | 12 +---- Modules/ROI/include/mitkROIMapper3D.h | 12 +---- .../ROI/include/mitkROIMapperLocalStorage.h | 43 +++++++++++++++++ Modules/ROI/src/mitkROIMapper2D.cpp | 11 ----- Modules/ROI/src/mitkROIMapper3D.cpp | 11 ----- Modules/ROI/src/mitkROIMapperLocalStorage.cpp | 46 +++++++++++++++++++ 7 files changed, 95 insertions(+), 42 deletions(-) create mode 100644 Modules/ROI/include/mitkROIMapperLocalStorage.h create mode 100644 Modules/ROI/src/mitkROIMapperLocalStorage.cpp diff --git a/Modules/ROI/files.cmake b/Modules/ROI/files.cmake index 9880797ab82..1de29e9b169 100644 --- a/Modules/ROI/files.cmake +++ b/Modules/ROI/files.cmake @@ -2,6 +2,7 @@ set(H_FILES include/mitkROI.h include/mitkROIMapper2D.h include/mitkROIMapper3D.h + include/mitkROIMapperLocalStorage.h src/mitkROIMapperHelper.h src/mitkROIObjectFactory.h ) @@ -11,5 +12,6 @@ set(CPP_FILES mitkROIMapper2D.cpp mitkROIMapper3D.cpp mitkROIMapperHelper.cpp + mitkROIMapperLocalStorage.cpp mitkROIObjectFactory.cpp ) diff --git a/Modules/ROI/include/mitkROIMapper2D.h b/Modules/ROI/include/mitkROIMapper2D.h index 0e8323592dd..173915aaeca 100644 --- a/Modules/ROI/include/mitkROIMapper2D.h +++ b/Modules/ROI/include/mitkROIMapper2D.h @@ -14,32 +14,24 @@ found in the LICENSE file. #define mitkROIMapper2D_h #include +#include #include #include -template -class vtkSmartPointer; - -class vtkPropAssembly; - namespace mitk { class MITKROI_EXPORT ROIMapper2D : public VtkMapper { - class LocalStorage : public Mapper::BaseLocalStorage + class LocalStorage : public ROIMapperLocalStorage { public: LocalStorage(); ~LocalStorage() override; - vtkPropAssembly* GetPropAssembly() const; - void SetPropAssembly(vtkPropAssembly* propAssembly); - const PlaneGeometry* GetLastPlaneGeometry() const; void SetLastPlaneGeometry(const PlaneGeometry* planeGeometry); protected: - vtkSmartPointer m_PropAssembly; PlaneGeometry::ConstPointer m_LastPlaneGeometry; }; diff --git a/Modules/ROI/include/mitkROIMapper3D.h b/Modules/ROI/include/mitkROIMapper3D.h index a4bb8f8618a..e57d3b148a1 100644 --- a/Modules/ROI/include/mitkROIMapper3D.h +++ b/Modules/ROI/include/mitkROIMapper3D.h @@ -14,27 +14,19 @@ found in the LICENSE file. #define mitkROIMapper3D_h #include +#include #include #include -#include -#include - namespace mitk { class MITKROI_EXPORT ROIMapper3D : public VtkMapper { - class LocalStorage : public Mapper::BaseLocalStorage + class LocalStorage : public ROIMapperLocalStorage { public: LocalStorage(); ~LocalStorage() override; - - vtkPropAssembly* GetPropAssembly() const; - void SetPropAssembly(vtkPropAssembly* propAssembly); - - protected: - vtkSmartPointer m_PropAssembly; }; public: diff --git a/Modules/ROI/include/mitkROIMapperLocalStorage.h b/Modules/ROI/include/mitkROIMapperLocalStorage.h new file mode 100644 index 00000000000..fddf8bfe674 --- /dev/null +++ b/Modules/ROI/include/mitkROIMapperLocalStorage.h @@ -0,0 +1,43 @@ +/*============================================================================ + +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 mitkROIMapperLocalStorage_h +#define mitkROIMapperLocalStorage_h + +#include + +template +class vtkSmartPointer; + +class vtkPropAssembly; + +namespace mitk +{ + class ROIMapperLocalStorage : public Mapper::BaseLocalStorage + { + public: + ROIMapperLocalStorage(); + ~ROIMapperLocalStorage() override; + + vtkPropAssembly* GetPropAssembly() const; + void SetPropAssembly(vtkPropAssembly* propAssembly); + + TimePointType GetLastTimePoint() const; + void SetLastTimePoint(TimePointType timePoint); + + protected: + vtkSmartPointer m_PropAssembly; + TimePointType m_LastTimePoint; + }; +} + +#endif diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp index c7784d6969f..f05c3fb5508 100644 --- a/Modules/ROI/src/mitkROIMapper2D.cpp +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -66,7 +66,6 @@ namespace } mitk::ROIMapper2D::LocalStorage::LocalStorage() - : m_PropAssembly(vtkSmartPointer::New()) { } @@ -74,16 +73,6 @@ mitk::ROIMapper2D::LocalStorage::~LocalStorage() { } -vtkPropAssembly* mitk::ROIMapper2D::LocalStorage::GetPropAssembly() const -{ - return m_PropAssembly; -} - -void mitk::ROIMapper2D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly) -{ - m_PropAssembly = propAssembly; -} - const mitk::PlaneGeometry* mitk::ROIMapper2D::LocalStorage::GetLastPlaneGeometry() const { return m_LastPlaneGeometry; diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index f68813ac34e..be630547409 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -21,7 +21,6 @@ found in the LICENSE file. #include mitk::ROIMapper3D::LocalStorage::LocalStorage() - : m_PropAssembly(vtkSmartPointer::New()) { } @@ -29,16 +28,6 @@ mitk::ROIMapper3D::LocalStorage::~LocalStorage() { } -vtkPropAssembly* mitk::ROIMapper3D::LocalStorage::GetPropAssembly() const -{ - return m_PropAssembly; -} - -void mitk::ROIMapper3D::LocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly) -{ - m_PropAssembly = propAssembly; -} - void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) { Superclass::SetDefaultProperties(node, renderer, override); diff --git a/Modules/ROI/src/mitkROIMapperLocalStorage.cpp b/Modules/ROI/src/mitkROIMapperLocalStorage.cpp new file mode 100644 index 00000000000..4c57abe8df3 --- /dev/null +++ b/Modules/ROI/src/mitkROIMapperLocalStorage.cpp @@ -0,0 +1,46 @@ +/*============================================================================ + +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 + +#include +#include + +mitk::ROIMapperLocalStorage::ROIMapperLocalStorage() + : m_PropAssembly(vtkSmartPointer::New()), + m_LastTimePoint(0.0) +{ +} + +mitk::ROIMapperLocalStorage::~ROIMapperLocalStorage() +{ +} + +vtkPropAssembly* mitk::ROIMapperLocalStorage::GetPropAssembly() const +{ + return m_PropAssembly; +} + +void mitk::ROIMapperLocalStorage::SetPropAssembly(vtkPropAssembly* propAssembly) +{ + m_PropAssembly = propAssembly; +} + +mitk::TimePointType mitk::ROIMapperLocalStorage::GetLastTimePoint() const +{ + return m_LastTimePoint; +} + +void mitk::ROIMapperLocalStorage::SetLastTimePoint(TimePointType timePoint) +{ + m_LastTimePoint = timePoint; +} From 037d9d3674e998f0450329883ad5f97fa139a42f Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 13:01:37 +0100 Subject: [PATCH 32/92] Adapt ROI mapper helpers to recent changes --- Modules/ROI/src/mitkROIMapperHelper.cpp | 16 +++++++++------- Modules/ROI/src/mitkROIMapperHelper.h | 8 ++++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/Modules/ROI/src/mitkROIMapperHelper.cpp b/Modules/ROI/src/mitkROIMapperHelper.cpp index cbf5b99bff5..685d0957940 100644 --- a/Modules/ROI/src/mitkROIMapperHelper.cpp +++ b/Modules/ROI/src/mitkROIMapperHelper.cpp @@ -19,26 +19,28 @@ found in the LICENSE file. #include -void mitk::ROIMapperHelper::ApplyIndividualProperties(const IPropertyProvider* properties, vtkActor* actor) +void mitk::ROIMapperHelper::ApplyIndividualProperties(const IPropertyProvider& properties, TimeStepType t, vtkActor* actor) { auto* property = actor->GetProperty(); property->SetRepresentationToWireframe(); property->LightingOff(); - if (auto colorProperty = GetConstProperty("color", properties); colorProperty != nullptr) + const auto contextName = std::to_string(t); + + if (auto colorProperty = GetConstProperty("color", properties, contextName); colorProperty != nullptr) { const auto color = colorProperty->GetColor(); property->SetColor(color[0], color[1], color[2]); } - if (auto opacityProperty = GetConstProperty("opacity", properties); opacityProperty != nullptr) + if (auto opacityProperty = GetConstProperty("opacity", properties, contextName); opacityProperty != nullptr) { const auto opacity = opacityProperty->GetValue(); property->SetOpacity(property->GetOpacity() * opacity); } - if (auto lineWidthProperty = GetConstProperty("lineWidth", properties); lineWidthProperty != nullptr) + if (auto lineWidthProperty = GetConstProperty("lineWidth", properties, contextName); lineWidthProperty != nullptr) { const auto lineWidth = lineWidthProperty->GetValue(); property->SetLineWidth(lineWidth); @@ -75,7 +77,7 @@ vtkSmartPointer mitk::ROIMapperHelper::CreateCaptionActor(con return captionActor; } -std::string mitk::ROIMapperHelper::ParseCaption(const std::string& captionTemplate, const ROI::Element& roi) +std::string mitk::ROIMapperHelper::ParseCaption(const std::string& captionTemplate, const ROI::Element& roi, TimeStepType t) { std::regex regex(R"(\{([^}]*)\})"); // Anything between curly braces (considered as placeholder). @@ -95,11 +97,11 @@ std::string mitk::ROIMapperHelper::ParseCaption(const std::string& captionTempla if (match[1] == "ID") { - caption.append(std::to_string(roi.ID)); + caption.append(std::to_string(roi.GetID())); } else { - auto property = roi.GetConstProperty(match[1]); + auto property = roi.GetConstProperty(match[1], std::to_string(t)); if (property.IsNotNull()) caption.append(property->GetValueAsString()); diff --git a/Modules/ROI/src/mitkROIMapperHelper.h b/Modules/ROI/src/mitkROIMapperHelper.h index 48161c27adb..e6239fba1ad 100644 --- a/Modules/ROI/src/mitkROIMapperHelper.h +++ b/Modules/ROI/src/mitkROIMapperHelper.h @@ -24,18 +24,18 @@ namespace mitk { namespace ROIMapperHelper { - void ApplyIndividualProperties(const IPropertyProvider* properties, vtkActor* actor); + void ApplyIndividualProperties(const IPropertyProvider& properties, TimeStepType t, vtkActor* actor); vtkSmartPointer CreateCaptionActor(const std::string& caption, const Point3D& attachmentPoint, vtkProperty* property, const DataNode* dataNode, const BaseRenderer* renderer); - std::string ParseCaption(const std::string& captionTemplate, const ROI::Element& roi); + std::string ParseCaption(const std::string& captionTemplate, const ROI::Element& roi, TimeStepType t = 0); void SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override); template - const T* GetConstProperty(const std::string& propertyKey, const mitk::IPropertyProvider* properties) + const T* GetConstProperty(const std::string& propertyKey, const mitk::IPropertyProvider& properties, const std::string& contextName = "") { - auto property = properties->GetConstProperty(propertyKey); + auto property = properties.GetConstProperty(propertyKey, contextName); if (property.IsNotNull()) return dynamic_cast(property.GetPointer()); From f2909bed0bf5bdbc4ec7d1fc81da164bf5561ea7 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 13:02:14 +0100 Subject: [PATCH 33/92] Support time steps in 2-d ROI mapper --- Modules/ROI/src/mitkROIMapper2D.cpp | 33 ++++++++++++++++------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp index f05c3fb5508..667a08bc194 100644 --- a/Modules/ROI/src/mitkROIMapper2D.cpp +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -99,46 +99,49 @@ mitk::ROIMapper2D::~ROIMapper2D() void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) { - const auto* dataNode = this->GetDataNode(); - - if (dataNode == nullptr) - return; - - auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); + const auto timePoint = renderer->GetWorldTimeGeometry()->TimeStepToTimePoint(renderer->GetTimeStep()); const auto* planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); + auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); + const auto* dataNode = this->GetDataNode(); if (localStorage->GetLastPlaneGeometry() != nullptr && localStorage->GetLastPlaneGeometry()->IsOnPlane(planeGeometry) && - localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime()) + localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime() && + localStorage->GetLastTimePoint() == timePoint) { return; } localStorage->SetLastPlaneGeometry(planeGeometry->Clone()); + localStorage->SetLastTimePoint(timePoint); - const auto* data = dynamic_cast(this->GetData()); + auto data = static_cast(this->GetData()); - if (data == nullptr) + if (!data->GetTimeGeometry()->IsValidTimePoint(timePoint)) return; + const auto t = data->GetTimeGeometry()->TimePointToTimeStep(timePoint); auto propAssembly = vtkSmartPointer::New(); if (dataNode->IsVisible(renderer)) { - const auto* geometry = data->GetGeometry(); + const auto* geometry = data->GetGeometry(t); const auto halfSpacing = geometry->GetSpacing() * 0.5f; auto plane = vtkSmartPointer::New(); plane->SetOrigin(planeGeometry->GetOrigin().data()); plane->SetNormal(planeGeometry->GetNormal().data()); - for (const auto& roi : *data) + for (const auto& [id, roi] : *data) { + if (!roi.HasTimeStep(t)) + continue; + Point3D min; - geometry->IndexToWorld(roi.Min, min); + geometry->IndexToWorld(roi.GetMin(t), min); min -= halfSpacing; Point3D max; - geometry->IndexToWorld(roi.Max, max); + geometry->IndexToWorld(roi.GetMax(t), max); max += halfSpacing; auto cube = vtkSmartPointer::New(); @@ -161,13 +164,13 @@ void mitk::ROIMapper2D::GenerateDataForRenderer(BaseRenderer* renderer) actor->SetMapper(mapper); this->ApplyColorAndOpacityProperties(renderer, actor); - ROIMapperHelper::ApplyIndividualProperties(roi.Properties, actor); + ROIMapperHelper::ApplyIndividualProperties(roi, t, actor); propAssembly->AddPart(actor); if (std::string caption; dataNode->GetStringProperty("caption", caption, renderer)) { - caption = ROIMapperHelper::ParseCaption(caption, roi); + caption = ROIMapperHelper::ParseCaption(caption, roi, t); if (!caption.empty()) { From 2884a0b45c1062636c73eef4069bebe01c73f48c Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 13:16:40 +0100 Subject: [PATCH 34/92] Support time steps in 3-d ROI mapper --- Modules/ROI/src/mitkROIMapper3D.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index be630547409..0c60991cbc3 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -44,21 +44,25 @@ mitk::ROIMapper3D::~ROIMapper3D() void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) { + const auto timePoint = renderer->GetWorldTimeGeometry()->TimeStepToTimePoint(renderer->GetTimeStep()); + const auto* planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); + auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); const auto* dataNode = this->GetDataNode(); - if (dataNode == nullptr) + if (localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime() && + localStorage->GetLastTimePoint() == timePoint) + { return; + } - auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); - - if (localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime()) - return; + localStorage->SetLastTimePoint(timePoint); - const auto* data = dynamic_cast(this->GetData()); + auto data = static_cast(this->GetData()); - if (data == nullptr) + if (!data->GetTimeGeometry()->IsValidTimePoint(timePoint)) return; + const auto t = data->GetTimeGeometry()->TimePointToTimeStep(timePoint); auto propAssembly = vtkSmartPointer::New(); if (dataNode->IsVisible(renderer)) @@ -66,14 +70,17 @@ void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) const auto* geometry = data->GetGeometry(); const auto halfSpacing = geometry->GetSpacing() * 0.5f; - for (const auto& roi : *data) + for (const auto& [id, roi] : *data) { + if (!roi.HasTimeStep(t)) + continue; + Point3D min; - geometry->IndexToWorld(roi.Min, min); + geometry->IndexToWorld(roi.GetMin(t), min); min -= halfSpacing; Point3D max; - geometry->IndexToWorld(roi.Max, max); + geometry->IndexToWorld(roi.GetMax(t), max); max += halfSpacing; auto cube = vtkSmartPointer::New(); @@ -87,7 +94,7 @@ void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) actor->SetMapper(mapper); this->ApplyColorAndOpacityProperties(renderer, actor); - ROIMapperHelper::ApplyIndividualProperties(roi.Properties, actor); + ROIMapperHelper::ApplyIndividualProperties(roi, t, actor); propAssembly->AddPart(actor); } From 5f10e62c2d7d926a9b7b96965e8540b6a97384ef Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 15:36:24 +0100 Subject: [PATCH 35/92] Implement writer for MITK ROI files General base data properties were removed from the file format in favor of explicit key value pairs like "Name" and "Caption". Otherwise a lot of effort is necessary to distinct base data properties that should be persistent and those who should not. --- Modules/ROI/autoload/IO/src/mitkROIIO.cpp | 186 ++++++++++++++-------- Modules/ROI/include/mitkROI.h | 5 + Modules/ROI/src/mitkROI.cpp | 87 ++++++++++ 3 files changed, 212 insertions(+), 66 deletions(-) diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp index 31a8e2c1eef..9cbc5fc05e4 100644 --- a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -15,36 +15,61 @@ found in the LICENSE file. #include #include -#include - #include #include namespace { - mitk::TimeGeometry::Pointer ReadGeometry(const nlohmann::json& jsonGeometry) + int CheckFileFormat(const nlohmann::json& json) + { + if ("MITK ROI" != json["FileFormat"].get()) + mitkThrow() << "Unknown file format (expected \"MITK ROI\")!"; + + auto version = json["Version"].get(); + + if (1 != version) + mitkThrow() << "Unknown file format version (expected version 1)!"; + + return version; + } + + mitk::Vector3D GetSize(const mitk::BaseGeometry* geometry) + { + auto bounds = geometry->GetBounds(); + + mitk::Vector3D result; + result[0] = bounds[1]; + result[1] = bounds[3]; + result[2] = bounds[5]; + + return result; + } + + void SetSize(mitk::BaseGeometry* geometry, const mitk::Vector3D& size) + { + mitk::BaseGeometry::BoundsArrayType bounds({ 0.0, size[0], 0.0, size[1], 0.0, size[2] }); + geometry->SetBounds(bounds); + } + + mitk::TimeGeometry::Pointer ReadGeometry(const nlohmann::json& jGeometry) { auto geometry = mitk::Geometry3D::New(); geometry->ImageGeometryOn(); - if (!jsonGeometry.is_object()) + if (!jGeometry.is_object()) mitkThrow() << "Geometry is expected to be a JSON object."; - if (jsonGeometry.contains("Origin")) - geometry->SetOrigin(jsonGeometry["Origin"].get()); + if (jGeometry.contains("Origin")) + geometry->SetOrigin(jGeometry["Origin"].get()); - if (jsonGeometry.contains("Spacing")) - geometry->SetSpacing(jsonGeometry["Spacing"].get()); + if (jGeometry.contains("Spacing")) + geometry->SetSpacing(jGeometry["Spacing"].get()); - if (jsonGeometry.contains("Size")) - { - auto size = jsonGeometry["Size"].get(); - mitk::BaseGeometry::BoundsArrayType bounds({ 0.0, size[0], 0.0, size[1], 0.0, size[2] }); - geometry->SetBounds(bounds); - } + if (jGeometry.contains("Size")) + SetSize(geometry, jGeometry["Size"].get()); - auto timeSteps = jsonGeometry.contains("TimeSteps") - ? jsonGeometry["TimeSteps"].get() + auto timeSteps = jGeometry.contains("TimeSteps") + ? jGeometry["TimeSteps"].get() : 1; auto result = mitk::ProportionalTimeGeometry::New(); @@ -52,6 +77,24 @@ namespace return result; } + + nlohmann::json WriteGeometry(const mitk::TimeGeometry* timeGeometry) + { + auto geometry = timeGeometry->GetGeometryForTimeStep(0); + + nlohmann::json result = { + { "Origin", geometry->GetOrigin() }, + { "Spacing", geometry->GetSpacing() }, + { "Size", GetSize(geometry) } + }; + + auto timeSteps = timeGeometry->CountTimeSteps(); + + if (timeSteps > 1) + result["TimeSteps"] = timeSteps; + + return result; + } } mitk::ROIIO::ROIIO() @@ -84,59 +127,21 @@ std::vector mitk::ROIIO::DoRead() try { - auto json = nlohmann::json::parse(*stream); + auto j = nlohmann::json::parse(*stream); - if ("MITK ROI" != json["FileFormat"].get()) - mitkThrow() << "Unknown file format (expected \"MITK ROI\")!"; + CheckFileFormat(j); - if (1 != json["Version"].get()) - mitkThrow() << "Unknown file format version (expected version 1)!"; + auto geometry = ReadGeometry(j["Geometry"]); + result->SetTimeGeometry(geometry); + + if (j.contains("Name")) + result->SetProperty("name", mitk::StringProperty::New(j["Name"].get())); - result->SetTimeGeometry(ReadGeometry(json["Geometry"])); - - if (json.contains("Properties")) - { - auto properties = mitk::PropertyList::New(); - properties->FromJSON(json["Properties"]); - result->GetPropertyList()->ConcatenatePropertyList(properties); - } - - for (const auto& jsonROI : json["ROIs"]) - { - ROI::Element roi(jsonROI["ID"].get()); - - if (jsonROI.contains("TimeSteps")) - { - for (const auto& jsonTimeStep : jsonROI["TimeSteps"]) - { - auto t = jsonTimeStep["t"].get(); - - roi.SetMin(jsonTimeStep["Min"].get(), t); - roi.SetMax(jsonTimeStep["Max"].get(), t); - - if (jsonTimeStep.contains("Properties")) - { - auto properties = mitk::PropertyList::New(); - properties->FromJSON(jsonTimeStep["Properties"]); - roi.SetProperties(properties, t); - } - } - } - else - { - roi.SetMin(jsonROI["Min"].get()); - roi.SetMax(jsonROI["Max"].get()); - } - - if (jsonROI.contains("Properties")) - { - auto properties = mitk::PropertyList::New(); - properties->FromJSON(jsonROI["Properties"]); - roi.SetDefaultProperties(properties); - } - - result->AddElement(roi); - } + if (j.contains("Caption")) + result->SetProperty("caption", mitk::StringProperty::New(j["Caption"].get())); + + for (const auto& roi : j["ROIs"]) + result->AddElement(roi.get()); } catch (const nlohmann::json::exception &e) { @@ -148,6 +153,55 @@ std::vector mitk::ROIIO::DoRead() void mitk::ROIIO::Write() { + auto input = dynamic_cast(this->GetInput()); + + if (input == nullptr) + mitkThrow() << "Invalid input for writing!"; + + if (input->GetNumberOfElements() == 0) + mitkThrow() << "No ROIs found!"; + + auto* stream = this->GetOutputStream(); + std::ofstream fileStream; + + if (stream == nullptr) + { + auto filename = this->GetOutputLocation(); + + if (filename.empty()) + mitkThrow() << "Neither an output stream nor an output filename was specified!"; + + fileStream.open(filename); + + if (!fileStream.is_open()) + mitkThrow() << "Could not open file \"" << filename << "\" for writing!"; + + stream = &fileStream; + } + + if (!stream->good()) + mitkThrow() << "Stream for writing is not good!"; + + nlohmann::ordered_json j = { + { "FileFormat", "MITK ROI" }, + { "Version", 1 }, + { "Name", input->GetProperty("name")->GetValueAsString() }, + { "Geometry", WriteGeometry(input->GetTimeGeometry()) } + }; + + auto caption = input->GetConstProperty("caption"); + + if (caption.IsNotNull()) + j["Caption"] = caption->GetValueAsString(); + + nlohmann::json rois; + + for (const auto& roi : *input) + rois.push_back(roi.second); + + j["ROIs"] = rois; + + *stream << std::setw(2) << j << std::endl; } mitk::ROIIO* mitk::ROIIO::IOClone() const diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h index 0e76d25c220..d580f5f9b5a 100644 --- a/Modules/ROI/include/mitkROI.h +++ b/Modules/ROI/include/mitkROI.h @@ -43,6 +43,8 @@ namespace mitk void SetID(unsigned int id); bool HasTimeStep(TimeStepType t) const; + bool HasTimeSteps() const; + std::vector GetTimeSteps() const; Point3D GetMin(TimeStepType t = 0) const; void SetMin(const Point3D& min, TimeStepType t = 0); @@ -99,6 +101,9 @@ namespace mitk private: ElementsType m_Elements; }; + + MITKROI_EXPORT void to_json(nlohmann::json& j, const ROI::Element& roi); + MITKROI_EXPORT void from_json(const nlohmann::json& j, ROI::Element& roi); } #endif diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp index 7243726046b..263392da58e 100644 --- a/Modules/ROI/src/mitkROI.cpp +++ b/Modules/ROI/src/mitkROI.cpp @@ -12,6 +12,74 @@ found in the LICENSE file. #include +void mitk::to_json(nlohmann::json& j, const ROI::Element& roi) +{ + j["ID"] = roi.GetID(); + + if (roi.HasTimeSteps()) + { + auto timeSteps = roi.GetTimeSteps(); + + for (const auto t : timeSteps) + { + nlohmann::json jTimeStep = { + { "t", t }, + { "Min", roi.GetMin(t) }, + { "Max", roi.GetMax(t) } + }; + + if (auto* properties = roi.GetProperties(t); properties != nullptr && !properties->IsEmpty()) + properties->ToJSON(jTimeStep["Properties"]); + + j["TimeSteps"].push_back(jTimeStep); + } + } + else + { + j["Min"] = roi.GetMin(); + j["Max"] = roi.GetMax(); + } + + if (auto* properties = roi.GetDefaultProperties(); properties != nullptr && !properties->IsEmpty()) + properties->ToJSON(j["Properties"]); +} + +void mitk::from_json(const nlohmann::json& j, ROI::Element& roi) +{ + auto id = j["ID"].get(); + roi.SetID(id); + + if (j.contains("TimeSteps")) + { + for (const auto& jTimeStep : j["TimeSteps"]) + { + auto t = jTimeStep["t"].get(); + + roi.SetMin(jTimeStep["Min"].get(), t); + roi.SetMax(jTimeStep["Max"].get(), t); + + if (jTimeStep.contains("Properties")) + { + auto properties = mitk::PropertyList::New(); + properties->FromJSON(jTimeStep["Properties"]); + roi.SetProperties(properties, t); + } + } + } + else + { + roi.SetMin(j["Min"].get()); + roi.SetMax(j["Max"].get()); + } + + if (j.contains("Properties")) + { + auto properties = mitk::PropertyList::New(); + properties->FromJSON(j["Properties"]); + roi.SetDefaultProperties(properties); + } +} + mitk::ROI::Element::Element() : Element(0) { @@ -38,6 +106,25 @@ bool mitk::ROI::Element::HasTimeStep(TimeStepType t) const return m_Min.count(t) != 0 && m_Max.count(t) != 0; } +bool mitk::ROI::Element::HasTimeSteps() const +{ + return m_Min.size() > 1 && m_Max.size() > 1; +} + +std::vector mitk::ROI::Element::GetTimeSteps() const +{ + std::vector result; + result.reserve(m_Min.size()); + + for (const auto& [t, min] : m_Min) + { + if (m_Max.count(t) != 0) + result.push_back(t); + } + + return result; +} + mitk::Point3D mitk::ROI::Element::GetMin(TimeStepType t) const { return m_Min.at(t); From 7f92ddc3b9f78991e724e6877272b4734d415691 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 16:17:34 +0100 Subject: [PATCH 36/92] Make nlohmann_json dependency public --- Modules/Core/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Core/CMakeLists.txt b/Modules/Core/CMakeLists.txt index 0ad7a726f54..74cada7281c 100644 --- a/Modules/Core/CMakeLists.txt +++ b/Modules/Core/CMakeLists.txt @@ -22,12 +22,12 @@ mitk_create_module( PACKAGE_DEPENDS PUBLIC Boost + nlohmann_json ITK|IOImageBase+SpatialObjects+Statistics #ITK|Statistics+Transform VTK|FiltersTexture+FiltersParallel+ImagingStencil+ImagingMath+InteractionStyle+RenderingOpenGL2+RenderingVolumeOpenGL2+RenderingFreeType+RenderingLabel+InteractionWidgets+IOGeometry+IOImage+IOXML PRIVATE ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOJPEG2000+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML - nlohmann_json tinyxml2 ${optional_private_package_depends} # Do not automatically create CppMicroServices initialization code. From cba93e162839e819e638b4ad302153a019cb977b Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 16:17:50 +0100 Subject: [PATCH 37/92] Add missing include --- Modules/Core/include/mitkPropertyList.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/Core/include/mitkPropertyList.h b/Modules/Core/include/mitkPropertyList.h index 996e7c10bae..8e1ccfc8cc5 100644 --- a/Modules/Core/include/mitkPropertyList.h +++ b/Modules/Core/include/mitkPropertyList.h @@ -14,6 +14,8 @@ found in the LICENSE file. #define mitkPropertyList_h #include +#include + #include namespace mitk From 1a9bf2aa9ee95758742e3ea9be2dc3bbc2447b74 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 16:18:16 +0100 Subject: [PATCH 38/92] Satisfy Clang compiler --- Modules/Core/include/mitkGenericLookupTable.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/Core/include/mitkGenericLookupTable.h b/Modules/Core/include/mitkGenericLookupTable.h index 43131a32ccd..9c2f99115d2 100644 --- a/Modules/Core/include/mitkGenericLookupTable.h +++ b/Modules/Core/include/mitkGenericLookupTable.h @@ -85,8 +85,7 @@ namespace mitk } } - template - friend void from_json(const nlohmann::json&, GenericLookupTable&); + friend void from_json<>(const nlohmann::json&, GenericLookupTable&); protected: LookupTableType m_LookupTable; From f04c0d712e8f9b7c3090812efcd02560fae7af94 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 16:33:55 +0100 Subject: [PATCH 39/92] Satisfy Clang compiler (this time for real) --- Modules/Core/include/mitkGenericLookupTable.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/Core/include/mitkGenericLookupTable.h b/Modules/Core/include/mitkGenericLookupTable.h index 9c2f99115d2..a450e3b1255 100644 --- a/Modules/Core/include/mitkGenericLookupTable.h +++ b/Modules/Core/include/mitkGenericLookupTable.h @@ -27,6 +27,9 @@ found in the LICENSE file. namespace mitk { + template class GenericLookupTable; + template void from_json(const nlohmann::json&, GenericLookupTable&); + /** * @brief Template class for generating lookup-tables * From 09a394c66855bf79b419c62f1e92e16cf6822366 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 17:52:20 +0100 Subject: [PATCH 40/92] Add missing include --- Modules/Core/include/mitkColorProperty.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Modules/Core/include/mitkColorProperty.h b/Modules/Core/include/mitkColorProperty.h index c4a3ae0be47..8b312423245 100644 --- a/Modules/Core/include/mitkColorProperty.h +++ b/Modules/Core/include/mitkColorProperty.h @@ -13,10 +13,13 @@ found in the LICENSE file. #ifndef mitkColorProperty_h #define mitkColorProperty_h -#include "mitkBaseProperty.h" +#include #include + #include +#include + namespace mitk { #ifdef _MSC_VER From 8f9396c71352f4b2d56b532b62abaeb550c59fe0 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 18:05:27 +0100 Subject: [PATCH 41/92] Remove unused parameter --- Modules/ROI/src/mitkROI.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp index 263392da58e..bd25ff2e846 100644 --- a/Modules/ROI/src/mitkROI.cpp +++ b/Modules/ROI/src/mitkROI.cpp @@ -364,6 +364,6 @@ bool mitk::ROI::VerifyRequestedRegion() return true; } -void mitk::ROI::SetRequestedRegion(const itk::DataObject* data) +void mitk::ROI::SetRequestedRegion(const itk::DataObject*) { } From 93acff4e90c8c5e0332e512877eea8a238798822 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 18:16:56 +0100 Subject: [PATCH 42/92] Add missing include --- Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp b/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp index f6e48f7dcc6..40ad9545b10 100644 --- a/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp +++ b/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp @@ -15,8 +15,7 @@ found in the LICENSE file. #include "mitkIGTLDevice.h" #include "mitkIGTLMessage.h" -//#include "mitkIGTTimeStamp.h" -//#include "mitkIGTException.h" +#include //Microservices #include From 82381087fa7b768afded8751ef9a27b3a4e00865 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 18:27:06 +0100 Subject: [PATCH 43/92] Remove unused function --- Modules/ROI/src/mitkROIMapper2D.cpp | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp index 667a08bc194..ce14288cca7 100644 --- a/Modules/ROI/src/mitkROIMapper2D.cpp +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -26,21 +26,6 @@ found in the LICENSE file. namespace { - std::string GetName(const mitk::ROI::Element& roi) - { - auto property = roi.GetConstProperty("name"); - - if (property.IsNotNull()) - { - auto nameProperty = dynamic_cast(property.GetPointer()); - - if (nameProperty != nullptr) - return nameProperty->GetValue(); - } - - return ""; - } - mitk::Point3D GetBottomLeftPoint(vtkPoints* points, mitk::BaseRenderer* renderer) { mitk::Point3D point = points->GetPoint(0); From b303d7ca9f9083f0cc2709aca8490b5875b104f7 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 18:45:03 +0100 Subject: [PATCH 44/92] Remove unused variable --- Modules/ROI/src/mitkROIMapper3D.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index 0c60991cbc3..c758c5fa971 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -45,7 +45,6 @@ mitk::ROIMapper3D::~ROIMapper3D() void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) { const auto timePoint = renderer->GetWorldTimeGeometry()->TimeStepToTimePoint(renderer->GetTimeStep()); - const auto* planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); const auto* dataNode = this->GetDataNode(); From 4aeabc52b019001a11aa5505708059d8cd899b99 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 18:45:27 +0100 Subject: [PATCH 45/92] Replace deprecated method call --- Modules/ROI/src/mitkROIMapper2D.cpp | 2 +- Modules/ROI/src/mitkROIMapper3D.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp index ce14288cca7..1435d4c6d71 100644 --- a/Modules/ROI/src/mitkROIMapper2D.cpp +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -177,7 +177,7 @@ void mitk::ROIMapper2D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, v auto* property = actor->GetProperty(); float opacity = 1.0f; - this->GetOpacity(opacity, renderer); + this->GetDataNode()->GetOpacity(opacity, renderer); property->SetOpacity(opacity); } diff --git a/Modules/ROI/src/mitkROIMapper3D.cpp b/Modules/ROI/src/mitkROIMapper3D.cpp index c758c5fa971..e8120de0e37 100644 --- a/Modules/ROI/src/mitkROIMapper3D.cpp +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -108,7 +108,7 @@ void mitk::ROIMapper3D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, v auto* property = actor->GetProperty(); float opacity = 1.0f; - this->GetOpacity(opacity, renderer); + this->GetDataNode()->GetOpacity(opacity, renderer); property->SetOpacity(opacity); } From 43a91fba572b0d6e369cb8ce5e7ca31b6b4f9e0b Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 19:42:03 +0100 Subject: [PATCH 46/92] Add missing include --- Modules/ModelFit/test/mitkModelFitInfoTest.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp index 47936184970..692892d0941 100644 --- a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp +++ b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp @@ -21,6 +21,7 @@ found in the LICENSE file. #include "mitkModelFitResultRelationRule.h" #include #include +#include mitk::modelFit::ModelFitInfo::UIDType ensureModelFitUID(mitk::BaseData * data) From 4f28789a8c0ec8631201d5eebb18d72b7cf661be Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 19:43:17 +0100 Subject: [PATCH 47/92] Implement JSON serialization for TubeGraphProperty --- Modules/TubeGraph/include/mitkTubeGraphProperty.h | 3 +++ .../TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp | 10 ++++++++++ 2 files changed, 13 insertions(+) diff --git a/Modules/TubeGraph/include/mitkTubeGraphProperty.h b/Modules/TubeGraph/include/mitkTubeGraphProperty.h index 87afa04b892..95fc651efee 100644 --- a/Modules/TubeGraph/include/mitkTubeGraphProperty.h +++ b/Modules/TubeGraph/include/mitkTubeGraphProperty.h @@ -120,6 +120,9 @@ namespace mitk std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + protected: TubeGraphProperty(); TubeGraphProperty(const TubeGraphProperty &other); diff --git a/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp b/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp index bbc2f89c39d..745be8cc85e 100644 --- a/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp +++ b/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp @@ -426,3 +426,13 @@ itk::LightObject::Pointer mitk::TubeGraphProperty::InternalClone() const itk::LightObject::Pointer result(new Self(*this)); return result; } + +bool mitk::TubeGraphProperty::ToJSON(nlohmann::json&) const +{ + return false; // Not implemented +} + +bool mitk::TubeGraphProperty::FromJSON(const nlohmann::json&) +{ + return false; // Not implemented +} From 16d103b465b2cd432cfd65ea34c9924ff8e28076 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 20:30:17 +0100 Subject: [PATCH 48/92] Add missing include --- Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h index d5412c1c7fc..d7c3378a01c 100644 --- a/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h +++ b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.h @@ -11,7 +11,9 @@ found in the LICENSE file. ============================================================================*/ #include + #include +#include namespace mitk { From 5fa57aac105e730d5ab6b4f6ccdd54472e1aa031 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 22:50:07 +0100 Subject: [PATCH 49/92] Implement JSON serialization for CameraIntrinsicsProperty --- .../CameraCalibration/mitkCameraIntrinsicsProperty.cpp | 10 ++++++++++ .../CameraCalibration/mitkCameraIntrinsicsProperty.h | 3 +++ 2 files changed, 13 insertions(+) diff --git a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp index e6ac3712c07..1e4f18c97f1 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp +++ b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp @@ -55,5 +55,15 @@ itk::LightObject::Pointer CameraIntrinsicsProperty::InternalClone() const return result; } +bool CameraIntrinsicsProperty::ToJSON(nlohmann::json&) const +{ + return false; +} + +bool CameraIntrinsicsProperty::FromJSON(const nlohmann::json&) +{ + return false; +} + } // namespace mitk diff --git a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h index c9929481568..23d40b6c69c 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h +++ b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h @@ -42,6 +42,9 @@ class MITKCAMERACALIBRATION_EXPORT CameraIntrinsicsProperty : public BasePropert std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: From 392eea25e27a7f32c3b989103721c3d838c677c3 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 8 Nov 2023 22:50:35 +0100 Subject: [PATCH 50/92] Add missing include --- Modules/US/USNavigation/mitkAbstractUltrasoundTrackerDevice.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Modules/US/USNavigation/mitkAbstractUltrasoundTrackerDevice.cpp b/Modules/US/USNavigation/mitkAbstractUltrasoundTrackerDevice.cpp index 0f9e8212389..b6ced66df9d 100644 --- a/Modules/US/USNavigation/mitkAbstractUltrasoundTrackerDevice.cpp +++ b/Modules/US/USNavigation/mitkAbstractUltrasoundTrackerDevice.cpp @@ -16,6 +16,7 @@ found in the LICENSE file. #include "mitkNavigationDataDisplacementFilter.h" #include "mitkNavigationDataSmoothingFilter.h" #include "mitkTrackingDeviceSource.h" +#include // US Control Interfaces #include "mitkUSControlInterfaceBMode.h" From 745ec315b69742d61abe9ff8e7accf92a2bdf4ab Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 01:34:33 +0100 Subject: [PATCH 51/92] Reorder includes Some nasty external dependency injects a "#define snprintf _snprintf" which messes up nlohmann_json. --- .../Python/autoload/PythonService/mitkPythonActivator.cpp | 2 +- Modules/Python/autoload/PythonService/mitkPythonService.h | 4 ++-- Modules/Python/mitkIPythonService.h | 6 ++++-- Modules/QtPython/QmitkCtkPythonShell.cpp | 2 +- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp b/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp index d58ba5cf273..f14ab932c22 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp +++ b/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp @@ -13,9 +13,9 @@ found in the LICENSE file. #define mitkPythonActivator_h // Microservices +#include "mitkPythonService.h" #include #include "usModuleContext.h" -#include "mitkPythonService.h" #include namespace mitk diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.h b/Modules/Python/autoload/PythonService/mitkPythonService.h index 31338dd8e15..6158d4be842 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonService.h +++ b/Modules/Python/autoload/PythonService/mitkPythonService.h @@ -12,10 +12,10 @@ found in the LICENSE file. #ifndef mitkPythonService_h #define mitkPythonService_h +#include +#include #include -#include "mitkIPythonService.h" #include -#include "mitkSurface.h" namespace mitk { diff --git a/Modules/Python/mitkIPythonService.h b/Modules/Python/mitkIPythonService.h index eb9c4d1dadd..e53457d24ad 100644 --- a/Modules/Python/mitkIPythonService.h +++ b/Modules/Python/mitkIPythonService.h @@ -13,11 +13,13 @@ found in the LICENSE file. #define mitkIPythonService_h // mitk +#include +#include #include -#include "mitkImage.h" + //for microservices #include -#include "mitkSurface.h" + #include diff --git a/Modules/QtPython/QmitkCtkPythonShell.cpp b/Modules/QtPython/QmitkCtkPythonShell.cpp index ebdbbf36d8c..c557fa1eac6 100644 --- a/Modules/QtPython/QmitkCtkPythonShell.cpp +++ b/Modules/QtPython/QmitkCtkPythonShell.cpp @@ -12,12 +12,12 @@ found in the LICENSE file. #include "QmitkCtkPythonShell.h" +#include #include #include #include #include #include -#include "mitkIPythonService.h" #include #include #include From 42b3b7f82a8da79e232c7432889c16789b67ed26 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 02:05:33 +0100 Subject: [PATCH 52/92] Improve detection for time-resolved ROIs --- Modules/ROI/src/mitkROI.cpp | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp index bd25ff2e846..1dfd1ad3739 100644 --- a/Modules/ROI/src/mitkROI.cpp +++ b/Modules/ROI/src/mitkROI.cpp @@ -108,7 +108,16 @@ bool mitk::ROI::Element::HasTimeStep(TimeStepType t) const bool mitk::ROI::Element::HasTimeSteps() const { - return m_Min.size() > 1 && m_Max.size() > 1; + // Check for multiple time steps. + if (m_Min.size() > 1 && m_Max.size() > 1) + return true; + + // Check for single time step that is not 0. + if (m_Min.size() >= 1 && m_Max.size() >= 1) + return m_Min.count(0) == 0 && m_Max.count(0) == 0; + + // Single time step 0. + return false; } std::vector mitk::ROI::Element::GetTimeSteps() const From 5b52d58976d90c4dc6562b88c7b31c4f6d051687 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 02:06:03 +0100 Subject: [PATCH 53/92] Remove outdated sentence --- .../3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md | 1 - 1 file changed, 1 deletion(-) diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md index ddbb5b926b6..b070e52c164 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md @@ -12,7 +12,6 @@ Optional properties of a segmentation task include a task name and description a The complete set of properties is specified further below in the file format specification. MITK Segmentation Task Lists must be considered experimental at the moment and are prone to change without any prior warning. -They are currently supported only in the MITK FlowBench application where a dedicated widget for task navigation and management appears if an MITK Segmentation Task List has been opened. ## File format From 9b3a81340e456b0a908e527b4c05ccbb1d18edde Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 02:07:24 +0100 Subject: [PATCH 54/92] Add ROI documentation --- .../3-DeveloperManual/Concepts/Concepts.dox | 1 + .../3-DeveloperManual/Concepts/MITKROIs.md | 24 ++++++ .../Core/include/mitkEnumerationProperty.h | 11 +++ .../include/mitkIPropertyDeserialization.h | 15 +++- Modules/Core/include/mitkPropertyList.h | 19 ++++ Modules/ROI/include/mitkROI.h | 86 +++++++++++++++++++ .../ROI/include/mitkROIMapperLocalStorage.h | 2 + Modules/ROI/src/mitkROIMapperHelper.h | 12 +++ 8 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox index ef6f9c5bfa6..2213eef822e 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox @@ -14,6 +14,7 @@ The following items describe some issues about MITK on a more abstract level. -# \subpage ReaderWriterPage -# \subpage MitkImagePage -# \subpage MITKSegmentationTaskListsPage + -# \subpage MITKROIsPage -# \subpage PropertiesPage -# \subpage GeometryOverviewPage -# \subpage PipelineingConceptPage diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md new file mode 100644 index 00000000000..7e73aa67e88 --- /dev/null +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md @@ -0,0 +1,24 @@ +# MITK ROIs {#MITKROIsPage} + +[TOC] + +## Overview + +MITK ROIs are a JSON-based file format defining a list of region of interests (ROIs). + +MITK ROIs must be considered experimental at the moment and are prone to change without any prior warning. + +## File format + +MITK ROIs are JSON files containing a JSON object as root, which must contain the two mandatory properties `FileFormat` and `Version`: + +~~~{.json} +{ + "FileFormat": "MITK ROI", + "Version": 1 +} +~~~ + +### ROIs + +... diff --git a/Modules/Core/include/mitkEnumerationProperty.h b/Modules/Core/include/mitkEnumerationProperty.h index 8ec695d872c..7771762a5b5 100644 --- a/Modules/Core/include/mitkEnumerationProperty.h +++ b/Modules/Core/include/mitkEnumerationProperty.h @@ -164,7 +164,18 @@ namespace mitk EnumIdsContainerType &GetEnumIds(); EnumStringsContainerType &GetEnumStrings(); + /** + * Serializes the property to JSON. + * @note Classes deriving from EnumerationProperty are covered by this implementation and do not + * need to override this method again. + */ bool ToJSON(nlohmann::json& j) const override; + + /** + * Deserializes the property to JSON. + * @note Classes deriving from EnumerationProperty are covered by this implementation and do not + * need to override this method again. + */ bool FromJSON(const nlohmann::json& j) override; using BaseProperty::operator=; diff --git a/Modules/Core/include/mitkIPropertyDeserialization.h b/Modules/Core/include/mitkIPropertyDeserialization.h index 8d1a43b8c28..7911bceb005 100644 --- a/Modules/Core/include/mitkIPropertyDeserialization.h +++ b/Modules/Core/include/mitkIPropertyDeserialization.h @@ -24,6 +24,13 @@ found in the LICENSE file. namespace mitk { + /** + * \ingroup MicroServices_Interfaces + * \brief Interface of property deserialization service. + * + * This service allows you to register custom property types (derived from BaseProperty) for deserialization. + * If a property type is not registered, it cannot be deserialized, e.g. when deserializing a PropertyList. + */ class MITKCORE_EXPORT IPropertyDeserialization { public: @@ -31,6 +38,13 @@ namespace mitk virtual BaseProperty::Pointer CreateInstance(const std::string& className) = 0; + /** + * \brief Register a custom property type for deserialization. + * + * The module activator of the module defining a property type is a good location to register + * custom property types of that module. See the implementation of MitkCoreActivator for + * examples. + */ template >> void RegisterProperty() { @@ -38,7 +52,6 @@ namespace mitk } protected: - virtual void InternalRegisterProperty(const BaseProperty* property) = 0; }; } diff --git a/Modules/Core/include/mitkPropertyList.h b/Modules/Core/include/mitkPropertyList.h index 8e1ccfc8cc5..763715f97bb 100644 --- a/Modules/Core/include/mitkPropertyList.h +++ b/Modules/Core/include/mitkPropertyList.h @@ -226,7 +226,26 @@ namespace mitk bool IsEmpty() const { return m_Properties.empty(); } virtual void Clear(); + /** + * @brief Serialize the property list to JSON. + * + * @note Properties of a certain type can only be deseralized again if their type has been + * registered via the IPropertyDeserialization core service. + * + * @sa CoreServices + * @sa IPropertyDeserialization::RegisterProperty + */ void ToJSON(nlohmann::json& j) const; + + /** + * @brief Deserialize the property list from JSON. + * + * @note Properties of a certain type can only be deseralized again if their type has been + * registered via the IPropertyDeserialization core service. + * + * @sa CoreServices + * @sa IPropertyDeserialization::RegisterProperty + */ void FromJSON(const nlohmann::json& j); protected: diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h index d580f5f9b5a..5546f6100ff 100644 --- a/Modules/ROI/include/mitkROI.h +++ b/Modules/ROI/include/mitkROI.h @@ -18,9 +18,48 @@ found in the LICENSE file. namespace mitk { + /** \brief A collection of region of interests (ROIs). + * + * ROIs, essentially defined by the minimum and maximum index coordinates of an axis-aligned box, are + * represented by the nested ROI::Element class. These index coordinates are relative to a common + * TimeGeometry. + * + * All ROIs are required to have a unique ID by which they can be accessed. + * + * ROIs can optionally have properties (PropertyList), also called default properties. In case of + * time-resolved ROIs, each time step can optionally have properties, too. These properties have + * precedence over the default properties. In other words, the default properties may contain + * fallback properties which are queried when a property is not defined at a certain time step. + * This allows for an opt-in dynamic appearance of ROIs over time, for example by changing + * color or opacity. + * + * ROIs are rendered both in 3-d and 2-d views as cubes, resp. cutting slices of these cubes. + * They support the following ROI::Element properties: + * + * - \c color (ColorProperty): Color of the cube + * - \c opacity (FloatProperty): Opacity of the cube + * - \c lineWidth (FloatProperty): %Line width of the cube edges + * + * ROIs display a customizable caption at their bottom-left corner. It is defined by the base data + * property \c caption (StringProperty). By default it is set to "{name} ({ID})". Braces + * are used to define placeholders which are replaced by their corresponding ROI::Element properties. + * {ID} is a special placeholder which will be replaced by the ID of the ROI::Element instead. + * The caption is allowed to include line breaks. Rendering of captions can be customized through the + * following data node properties: + * + * - \c font.size (IntProperty) Font size in points + * - \c font.bold (BoolProperty) Bold font style + * - \c font.italic (BoolProperty) Italic font style + * + * See \ref MITKROIsPage for details on the JSON-based MITK %ROI file format. + */ class MITKROI_EXPORT ROI : public BaseData { public: + /** \brief Encapsulates a single (possibly time-resolved) %ROI. + * + * \sa ROI + */ class MITKROI_EXPORT Element : public IPropertyOwner { public: @@ -31,19 +70,58 @@ namespace mitk explicit Element(unsigned int id); ~Element() = default; + /** \brief Get a const property. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ BaseProperty::ConstPointer GetConstProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = true) const override; + + /** \brief Get all property keys. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ std::vector GetPropertyKeys(const std::string& contextName = "", bool includeDefaultContext = false) const override; + + /** \brief Get all property context names (stringified time steps). + */ std::vector GetPropertyContextNames() const override; + /** \brief Get a property. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ BaseProperty* GetNonConstProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = true) override; + + /** \brief Set a property. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ void SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; + + /** \brief Remove a property. + * + * \note A time step can be specified as context. Use \c std::to_string() to convert a time step to a context name. + * An empty context name addresses the default properties. + */ void RemoveProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; unsigned int GetID() const; void SetID(unsigned int id); + /** \brief Check if the %ROI is defined for a certain time step. + */ bool HasTimeStep(TimeStepType t) const; + + /** \brief Check if the %ROI can be considered time-resolved. + */ bool HasTimeSteps() const; + + /** \brief Get all valid time steps that have a minimum point and a maximum point. + */ std::vector GetTimeSteps() const; Point3D GetMin(TimeStepType t = 0) const; @@ -55,7 +133,10 @@ namespace mitk PropertyList* GetDefaultProperties() const; void SetDefaultProperties(PropertyList* properties); + /** \brief Get properties for a certain time step or \c nullptr if absent. + */ PropertyList* GetProperties(TimeStepType t = 0) const; + void SetProperties(PropertyList* properties, TimeStepType t = 0); private: @@ -75,6 +156,11 @@ namespace mitk using ConstIterator = ElementsType::const_iterator; size_t GetNumberOfElements() const; + + /** \brief Add a ROI::Element to the collection. + * + * \note The ID of the ROI::Element must be set to a unique number in advance. + */ void AddElement(const Element& element); const Element& GetElement(unsigned int id) const; diff --git a/Modules/ROI/include/mitkROIMapperLocalStorage.h b/Modules/ROI/include/mitkROIMapperLocalStorage.h index fddf8bfe674..567d1eca8e0 100644 --- a/Modules/ROI/include/mitkROIMapperLocalStorage.h +++ b/Modules/ROI/include/mitkROIMapperLocalStorage.h @@ -22,6 +22,8 @@ class vtkPropAssembly; namespace mitk { + /** \brief Common base class for both 2-d and 3-d %ROI mapper local storages. + */ class ROIMapperLocalStorage : public Mapper::BaseLocalStorage { public: diff --git a/Modules/ROI/src/mitkROIMapperHelper.h b/Modules/ROI/src/mitkROIMapperHelper.h index e6239fba1ad..a529cc5b991 100644 --- a/Modules/ROI/src/mitkROIMapperHelper.h +++ b/Modules/ROI/src/mitkROIMapperHelper.h @@ -24,14 +24,26 @@ namespace mitk { namespace ROIMapperHelper { + /** \brief Apply %ROI properties at a certain time step to the given actor. + */ void ApplyIndividualProperties(const IPropertyProvider& properties, TimeStepType t, vtkActor* actor); + /** \brief Create an actor for the %ROI caption located at a certain attachment point considering several properties. + */ vtkSmartPointer CreateCaptionActor(const std::string& caption, const Point3D& attachmentPoint, vtkProperty* property, const DataNode* dataNode, const BaseRenderer* renderer); + /** \brief Substitute all placeholders in a caption with corresponding property values. + * + * \sa ROI + */ std::string ParseCaption(const std::string& captionTemplate, const ROI::Element& roi, TimeStepType t = 0); + /** \brief Set common default properties for both 2-d and 3-d %ROI mappers. + */ void SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override); + /** \brief Syntactic sugar for getting properties. + */ template const T* GetConstProperty(const std::string& propertyKey, const mitk::IPropertyProvider& properties, const std::string& contextName = "") { From 3342cc69bb7abb952e6cb29148a8d05445cad4f9 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 02:42:40 +0100 Subject: [PATCH 55/92] Reorder includes --- Modules/Python/autoload/PythonService/mitkPythonService.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.h b/Modules/Python/autoload/PythonService/mitkPythonService.h index 6158d4be842..436bc0ca084 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonService.h +++ b/Modules/Python/autoload/PythonService/mitkPythonService.h @@ -12,9 +12,9 @@ found in the LICENSE file. #ifndef mitkPythonService_h #define mitkPythonService_h +#include #include #include -#include #include namespace mitk From 52c74468c53d1df3cb95423effab4289355f3b54 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 02:55:06 +0100 Subject: [PATCH 56/92] Reorder includes --- Modules/QtPython/QmitkCtkPythonShell.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Modules/QtPython/QmitkCtkPythonShell.cpp b/Modules/QtPython/QmitkCtkPythonShell.cpp index c557fa1eac6..629a9cce873 100644 --- a/Modules/QtPython/QmitkCtkPythonShell.cpp +++ b/Modules/QtPython/QmitkCtkPythonShell.cpp @@ -12,8 +12,8 @@ found in the LICENSE file. #include "QmitkCtkPythonShell.h" -#include #include +#include #include #include #include From 0ce20cf8f9f999901a8290e3ccc803b775001887 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 08:28:48 +0100 Subject: [PATCH 57/92] Make inclusion of ctkAbstractPythonManager.h compatible to nlohmann_json --- Modules/Python/autoload/PythonService/mitkPythonService.h | 4 ++++ Modules/QtPython/QmitkCtkPythonShell.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.h b/Modules/Python/autoload/PythonService/mitkPythonService.h index 436bc0ca084..c98f71a64c6 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonService.h +++ b/Modules/Python/autoload/PythonService/mitkPythonService.h @@ -13,6 +13,10 @@ found in the LICENSE file. #define mitkPythonService_h #include +#ifdef snprintf +#undef snprintf +#endif + #include #include #include diff --git a/Modules/QtPython/QmitkCtkPythonShell.cpp b/Modules/QtPython/QmitkCtkPythonShell.cpp index 629a9cce873..dec9e9168ad 100644 --- a/Modules/QtPython/QmitkCtkPythonShell.cpp +++ b/Modules/QtPython/QmitkCtkPythonShell.cpp @@ -13,6 +13,10 @@ found in the LICENSE file. #include "QmitkCtkPythonShell.h" #include +#ifdef snprintf +#undef snprintf +#endif + #include #include #include From bb2783265f118270a6a6f9e931e53c3bb9f08280 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 21:39:26 +0100 Subject: [PATCH 58/92] Fix bounding shape handle size --- .../src/Rendering/mitkBoundingShapeVtkMapper2D.cpp | 2 +- .../src/Rendering/mitkBoundingShapeVtkMapper3D.cpp | 2 +- .../src/internal/QmitkImageCropperView.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp index 4d6afbb5a33..0f09d1916e7 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp @@ -340,7 +340,7 @@ void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer *rende if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else - initialHandleSize = 1.0 / 40.0; + initialHandleSize = 0.02; mitk::Point2D displaySize = renderer->GetDisplaySizeInMM(); double handleSize = ((displaySize[0] + displaySize[1]) / 2.0) * initialHandleSize; diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp index 75d303f210a..00b118e15c0 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp @@ -228,7 +228,7 @@ void mitk::BoundingShapeVtkMapper3D::GenerateDataForRenderer(BaseRenderer *rende if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else - initialHandleSize = 1.0 / 40.0; + initialHandleSize = 0.02; double handlesize = ((camera->GetDistance() * std::tan(vtkMath::RadiansFromDegrees(camera->GetViewAngle()))) / 2.0) * diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp index 6d4155a0844..d56adc2f179 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp @@ -262,7 +262,7 @@ void QmitkImageCropperView::OnCreateNewBoundingBox() boundingBoxNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); boundingBoxNode->SetProperty("opacity", mitk::FloatProperty::New(0.6)); boundingBoxNode->SetProperty("layer", mitk::IntProperty::New(99)); - boundingBoxNode->AddProperty("handle size factor", mitk::DoubleProperty::New(1.0 / 40.0)); + boundingBoxNode->AddProperty("Bounding Shape.Handle Size Factor", mitk::DoubleProperty::New(0.02)); boundingBoxNode->SetBoolProperty("pickable", true); if (!this->GetDataStorage()->Exists(boundingBoxNode)) From eb354f334d4cf7f66c50da4ad3e0adcde68647e4 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 21:39:57 +0100 Subject: [PATCH 59/92] Change bounding shape handles from spheres to cubes --- .../include/mitkBoundingShapeVtkMapper2D.h | 4 ++-- .../include/mitkBoundingShapeVtkMapper3D.h | 5 ----- .../Rendering/mitkBoundingShapeVtkMapper2D.cpp | 18 ++++-------------- .../Rendering/mitkBoundingShapeVtkMapper3D.cpp | 9 +++++---- 4 files changed, 11 insertions(+), 25 deletions(-) diff --git a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h index f3eebac5e38..1c67809032e 100644 --- a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h +++ b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h @@ -23,7 +23,7 @@ found in the LICENSE file. #include #include #include -#include +#include namespace mitk { @@ -46,7 +46,7 @@ namespace mitk vtkSmartPointer m_Cutter; vtkSmartPointer m_CuttingPlane; unsigned int m_LastSliceNumber; - std::vector> m_Handles; + std::vector> m_Handles; vtkSmartPointer m_PropAssembly; double m_ZoomFactor; diff --git a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h index 4e800576b04..3af3bdfe701 100644 --- a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h +++ b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h @@ -14,13 +14,8 @@ found in the LICENSE file. #define mitkBoundingShapeVtkMapper3D_h #include - #include -#include -#include -#include - namespace mitk { class MITKBOUNDINGSHAPE_EXPORT BoundingShapeVtkMapper3D : public VtkMapper diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp index 0f09d1916e7..f2a3cf330ca 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp @@ -17,27 +17,15 @@ found in the LICENSE file. #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 @@ -89,7 +77,7 @@ mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage() m_Cutter->SetCutFunction(m_CuttingPlane); for (int i = 0; i < 6; ++i) - m_Handles.push_back(CreateHandle()); + m_Handles.push_back(vtkSmartPointer::New()); m_PropAssembly->AddPart(m_Actor); m_PropAssembly->AddPart(m_HandleActor); @@ -365,7 +353,9 @@ void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer *rende { Point3D handleCenter = m_Impl->HandlePropertyList[handleIdx].GetPosition(); - handle->SetRadius(handleSize); + handle->SetXLength(handleSize); + handle->SetYLength(handleSize); + handle->SetZLength(handleSize); handle->SetCenter(handleCenter[0], handleCenter[1], handleCenter[2]); // show handles only if the corresponding face is aligned to the render window diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp index 00b118e15c0..ce0053e4e03 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp @@ -20,7 +20,6 @@ found in the LICENSE file. #include #include #include -#include #include namespace mitk @@ -36,7 +35,7 @@ namespace mitk LocalStorage(const LocalStorage &) = delete; LocalStorage &operator=(const LocalStorage &) = delete; - std::vector> Handles; + std::vector> Handles; vtkSmartPointer Actor; vtkSmartPointer HandleActor; vtkSmartPointer SelectedHandleActor; @@ -66,7 +65,7 @@ mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::LocalStorage() PropAssembly(vtkSmartPointer::New()) { for (int i = 0; i < 6; i++) - Handles.push_back(vtkSmartPointer::New()); + Handles.push_back(vtkSmartPointer::New()); } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::~LocalStorage() @@ -250,7 +249,9 @@ void mitk::BoundingShapeVtkMapper3D::GenerateDataForRenderer(BaseRenderer *rende { Point3D handlecenter = m_Impl->HandlePropertyList[i].GetPosition(); handle->SetCenter(handlecenter[0], handlecenter[1], handlecenter[2]); - handle->SetRadius(handlesize); + handle->SetXLength(handlesize); + handle->SetYLength(handlesize); + handle->SetZLength(handlesize); handle->Update(); if (activeHandleId == nullptr) { From 7a807cbe2f44aaf7c70541da06c1935bee0811ef Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 23:42:14 +0100 Subject: [PATCH 60/92] Add icon for GeometryData --- .../QtWidgets/resource/GeometryDataIcon.svg | 19 +++++++++++++++++++ Modules/QtWidgets/resource/Qmitk.qrc | 1 + .../src/QmitkNodeDescriptorManager.cpp | 4 ++++ 3 files changed, 24 insertions(+) create mode 100644 Modules/QtWidgets/resource/GeometryDataIcon.svg diff --git a/Modules/QtWidgets/resource/GeometryDataIcon.svg b/Modules/QtWidgets/resource/GeometryDataIcon.svg new file mode 100644 index 00000000000..090de6c5ef0 --- /dev/null +++ b/Modules/QtWidgets/resource/GeometryDataIcon.svg @@ -0,0 +1,19 @@ + + + + + +image/svg+xml + + + + + + + + + + + + + diff --git a/Modules/QtWidgets/resource/Qmitk.qrc b/Modules/QtWidgets/resource/Qmitk.qrc index e2c136c4b7d..f284857d3e2 100644 --- a/Modules/QtWidgets/resource/Qmitk.qrc +++ b/Modules/QtWidgets/resource/Qmitk.qrc @@ -31,5 +31,6 @@ visible.svg SegmentationTaskListIcon.svg ROIIcon.svg + GeometryDataIcon.svg diff --git a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp index 8e6fbaa8874..ab142499b03 100644 --- a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp +++ b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp @@ -50,6 +50,10 @@ void QmitkNodeDescriptorManager::Initialize() auto isROI = mitk::NodePredicateDataType::New("ROI"); AddDescriptor(new QmitkNodeDescriptor("ROI", roiIcon, isROI, this)); + auto geometryDataIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/GeometryDataIcon.svg")); + auto isGeometryData = mitk::NodePredicateDataType::New("GeometryData"); + AddDescriptor(new QmitkNodeDescriptor("GeometryData", geometryDataIcon, isGeometryData, this)); + auto isPointSet = mitk::NodePredicateDataType::New("PointSet"); AddDescriptor(new QmitkNodeDescriptor(tr("PointSet"), QString(":/Qmitk/PointSet_48.png"), isPointSet, this)); From 2018a9c821c9250672dab9d6cf455cfab2025750 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 23:43:06 +0100 Subject: [PATCH 61/92] Consistently use "bounding box" instead of "bounding shape" --- .../UserManual/QmitkImageCropper.dox | 16 ++++++++-------- .../src/internal/QmitkImageCropperView.cpp | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) 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 91a2f08bc42..427ed3baf69 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox +++ b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox @@ -5,19 +5,19 @@ \section org_mitk_views_imagecropperUsage Usage -The Image Cropper Plugin allows to crop and mask subvolumes out of the original image volume by defining a cubic bounding shape. +The Image Cropper Plugin allows to crop and mask subvolumes out of the original image volume by defining a cubic bounding box. \imageMacro{BoundingBox_ImageCropperView.png,"Bounding Shape.",12.00} -A new bounding shape can be created by selecting an image and pressing the 'New' button. The bounding shape appears as a child node in the data manager. Alternatively, an existing bounding shape can be selected. +A new bounding box can be created by selecting an image and pressing the 'New' button. The bounding box appears as a child node in the data manager. Alternatively, an existing bounding box can be selected. \imageMacro{Basic_ImageCropperView.png,"Basic Settings.",7.09} -This bounding shape can be placed at an arbitrary position in the volume and can be easily adjusted by using the handles on each of the faces. When activated, the handles are shown in red, otherwise, they are colored white. Hovering over either the shape or a single handle allows modifying the bounding shape. Moving the handles changes the respective extent of the bounding shape, whereas moving the shape itself changes its position. +This bounding 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. When activated, the handles are shown in red, otherwise, they are colored white. Hovering over either the box or a single handle allows modifying the bounding box. Moving the handles changes the respective extent of the bounding box, whereas moving the box itself changes its position. -As soon as the bounding shape is placed at the desired position, pressing the button 'Crop' creates a new image assigned to the original image as a child node containing only the selected subvolume. The size of the subvolume equals the size of the bounding shape. -Pressing the 'Mask' button keeps the original image size but masks out the area not contained within the bounding shape bounds. +As soon as the bounding box is placed at the desired position, pressing the button 'Crop' creates a new image assigned to the original image as a 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. In the case of 3D+t images, the whole time series is cropped by default. @@ -28,7 +28,7 @@ In the advanced settings view you find additional features: \subsection org_mitk_views_imagecropperMaskOutsidePixel Mask with outside pixel -Assigns the value of the voxels outside of the bounding shape when 'Mask' is used. +Assigns the value of the voxels outside of the bounding box when 'Mask' is used. \subsection org_mitk_views_imagecropperAdvancedOverwrite Overwrite original image @@ -45,8 +45,8 @@ If the checkbox 'Only crop current time step' is ticked, the xD + t image is red Cropping 2D images is not supported unless the are 3D images containing only a single slice. The user will be notified by a warning and the input is handled as a single label image. -Right now changing the shape or rotation of the bounding shape is not supported but might be integrated in the future. +Right now changing the rotation of the bounding box is not supported. -Furthermore, a warning appears when the bounding shape is not aligned with the image. In this case, the handles can not be used correctly and get deactivated. You can continue to alter them by performing a 'Reinit' on the image. +Furthermore, a warning appears when the bounding box is not aligned with the image. In this case, the handles can not be used correctly and get deactivated. You can continue to alter them by performing a 'Reinit' on the image. */ diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp index d56adc2f179..235f9dd1755 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp @@ -237,7 +237,7 @@ void QmitkImageCropperView::OnCreateNewBoundingBox() return; } - QString name = QString::fromStdString(imageNode->GetName() + " Bounding Shape"); + QString name = QString::fromStdString(imageNode->GetName() + " Bounding Box"); auto boundingShape = this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&name](const mitk::DataNode *node) { From 0c7ca73b00b37fb39f4de2621aea29678469df94 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 9 Nov 2023 23:58:19 +0100 Subject: [PATCH 62/92] Fix bounding shape mapper property connection For better contrast to the variety of grayscale images, the bounding box color is now red instead of white. It also matches the color scheme for the bounding shape interaction handlers. --- .../mitkBoundingShapeInteractor.cpp | 2 +- .../mitkBoundingShapeVtkMapper2D.cpp | 21 ++++++++++------ .../mitkBoundingShapeVtkMapper3D.cpp | 25 ++++++++----------- .../src/internal/QmitkImageCropperView.cpp | 2 -- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp index 9c61cf79aee..27a7210bb2b 100644 --- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp +++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp @@ -137,7 +137,7 @@ void mitk::BoundingShapeInteractor::DataNodeChanged() 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->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 0.0, 0.0)); newInputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(true)); newInputNode->AddProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp index f2a3cf330ca..5114a482016 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp @@ -59,7 +59,6 @@ mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage() m_ZoomFactor(1.0) { m_Actor->SetMapper(m_Mapper); - m_Actor->GetProperty()->SetOpacity(0.3); m_Actor->VisibilityOn(); m_HandleActor->SetMapper(m_HandleMapper); @@ -127,6 +126,7 @@ void mitk::BoundingShapeVtkMapper2D::Update(mitk::BaseRenderer *renderer) void mitk::BoundingShapeVtkMapper2D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); + node->AddProperty("opacity", FloatProperty::New(0.2f), renderer, overwrite); } mitk::BoundingShapeVtkMapper2D::BoundingShapeVtkMapper2D() : m_Impl(new Impl) @@ -406,12 +406,8 @@ void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer *rende 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]); - } + + this->ApplyColorAndOpacityProperties(renderer, localStorage->m_Actor); if (activeHandleId != nullptr) { @@ -452,6 +448,15 @@ vtkProp *mitk::BoundingShapeVtkMapper2D::GetVtkProp(BaseRenderer *renderer) return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->m_PropAssembly; } -void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *, vtkActor *) +void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { + auto* property = actor->GetProperty(); + + std::array color = { 1.0, 0.0, 0.0 }; + this->GetDataNode()->GetColor(color.data(), renderer); + property->SetColor(color[0], color[1], color[2]); + + float opacity = 0.2f; + this->GetDataNode()->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); } diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp index ce0053e4e03..b07e626fa21 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp @@ -75,6 +75,7 @@ mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::~LocalStorage() void mitk::BoundingShapeVtkMapper3D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); + node->AddProperty("opacity", FloatProperty::New(0.2f), renderer, overwrite); } mitk::BoundingShapeVtkMapper3D::BoundingShapeVtkMapper3D() : m_Impl(new Impl) @@ -86,9 +87,17 @@ mitk::BoundingShapeVtkMapper3D::~BoundingShapeVtkMapper3D() delete m_Impl; } -void mitk::BoundingShapeVtkMapper3D::ApplyColorAndOpacityProperties(BaseRenderer*, vtkActor*) +void mitk::BoundingShapeVtkMapper3D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { - //Superclass::ApplyColorAndOpacityProperties(renderer, actor); + auto* property = actor->GetProperty(); + + std::array color = { 1.0, 0.0, 0.0 }; + this->GetDataNode()->GetColor(color.data(), renderer); + property->SetColor(color[0], color[1], color[2]); + + float opacity = 0.2f; + this->GetDataNode()->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); } void mitk::BoundingShapeVtkMapper3D::ApplyBoundingShapeProperties(BaseRenderer *renderer, vtkActor *actor) @@ -284,14 +293,6 @@ void mitk::BoundingShapeVtkMapper3D::GenerateDataForRenderer(BaseRenderer *rende localStorage->Actor->SetMapper(mapper); localStorage->Actor->GetMapper()->SetInputDataObject(polydata); - localStorage->Actor->GetProperty()->SetOpacity(0.3); - - mitk::ColorProperty::Pointer selectedColor = dynamic_cast(dataNode->GetProperty("color")); - if (selectedColor != nullptr) - { - mitk::Color color = selectedColor->GetColor(); - localStorage->Actor->GetProperty()->SetColor(color[0], color[1], color[2]); - } localStorage->HandleActor->SetMapper(handlemapper); if (activeHandleId == nullptr) @@ -307,11 +308,7 @@ void mitk::BoundingShapeVtkMapper3D::GenerateDataForRenderer(BaseRenderer *rende this->ApplyColorAndOpacityProperties(renderer, localStorage->Actor); this->ApplyBoundingShapeProperties(renderer, localStorage->Actor); - - this->ApplyColorAndOpacityProperties(renderer, localStorage->HandleActor); this->ApplyBoundingShapeProperties(renderer, localStorage->HandleActor); - - this->ApplyColorAndOpacityProperties(renderer, localStorage->SelectedHandleActor); this->ApplyBoundingShapeProperties(renderer, localStorage->SelectedHandleActor); // apply properties read from the PropertyList diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp index 235f9dd1755..c8ae768dbde 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp @@ -259,8 +259,6 @@ void QmitkImageCropperView::OnCreateNewBoundingBox() auto boundingBoxNode = mitk::DataNode::New(); boundingBoxNode->SetData(boundingBox); boundingBoxNode->SetProperty("name", mitk::StringProperty::New(name.toStdString())); - boundingBoxNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); - boundingBoxNode->SetProperty("opacity", mitk::FloatProperty::New(0.6)); boundingBoxNode->SetProperty("layer", mitk::IntProperty::New(99)); boundingBoxNode->AddProperty("Bounding Shape.Handle Size Factor", mitk::DoubleProperty::New(0.02)); boundingBoxNode->SetBoolProperty("pickable", true); From 405c6b86c575c3c6cd3bc2a049b2c289938eee68 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Fri, 10 Nov 2023 00:37:04 +0100 Subject: [PATCH 63/92] Add "Convert to ROI" action to GeometryData nodes --- .../CMakeLists.txt | 2 +- .../org.mitk.gui.qt.imagecropper/files.cmake | 2 + .../manifest_headers.cmake | 2 +- .../org.mitk.gui.qt.imagecropper/plugin.xml | 4 + .../QmitkConvertGeometryDataToROIAction.cpp | 96 +++++++++++++++++++ .../QmitkConvertGeometryDataToROIAction.h | 37 +++++++ .../src/internal/mitkPluginActivator.cpp | 2 + 7 files changed, 143 insertions(+), 2 deletions(-) create mode 100644 Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp create mode 100644 Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.h diff --git a/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt b/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt index 4d2a1eebde7..c6772ccead6 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt @@ -3,6 +3,6 @@ project(org_mitk_gui_qt_imagecropper) mitk_create_plugin( EXPORT_DIRECTIVE MITK_QT_IMAGECROPPER EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDS MitkQtWidgetsExt MitkBoundingShape MitkMultilabel + MODULE_DEPENDS MitkQtWidgetsExt MitkBoundingShape MitkMultilabel MitkROI ) diff --git a/Plugins/org.mitk.gui.qt.imagecropper/files.cmake b/Plugins/org.mitk.gui.qt.imagecropper/files.cmake index 59435c58591..58e89a3b01b 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/files.cmake +++ b/Plugins/org.mitk.gui.qt.imagecropper/files.cmake @@ -1,6 +1,7 @@ set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkImageCropperView.cpp + QmitkConvertGeometryDataToROIAction.cpp ) set(UI_FILES @@ -10,6 +11,7 @@ set(UI_FILES set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkImageCropperView.h + src/internal/QmitkConvertGeometryDataToROIAction.h ) set(CACHED_RESOURCE_FILES diff --git a/Plugins/org.mitk.gui.qt.imagecropper/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.imagecropper/manifest_headers.cmake index 8211a4fb19e..dc360f2d351 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.imagecropper/manifest_headers.cmake @@ -2,4 +2,4 @@ set(Plugin-Name "MITK Image Cropper") set(Plugin-Version "1.0.0") set(Plugin-Vendor "German Cancer Research Center (DKFZ)") set(Plugin-ContactAddress "https://www.mitk.org") -set(Require-Plugin org.mitk.gui.qt.common) +set(Require-Plugin org.mitk.gui.qt.common org.mitk.gui.qt.application) diff --git a/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml b/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml index ccc04b49d1f..3974b6148f0 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml +++ b/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml @@ -15,6 +15,10 @@ + + + + diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp new file mode 100644 index 00000000000..50fcc3a3e11 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp @@ -0,0 +1,96 @@ +/*============================================================================ + +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 "QmitkConvertGeometryDataToROIAction.h" + +#include +#include +#include + +#include + +namespace +{ + void handleInvalidNodeSelection() + { + auto message = QStringLiteral("All selected bounding boxes must be child nodes of a single common reference image!"); + MITK_ERROR << message; + QMessageBox::warning(nullptr, QStringLiteral("Convert to ROI"), message); + } + + std::vector filterNodeSelection(const QList& selectedNodes, const mitk::DataStorage* dataStorage) + { + std::vector result; + + std::copy_if(selectedNodes.cbegin(), selectedNodes.cend(), std::back_inserter(result), [](const mitk::DataNode* node) { + return node != nullptr && dynamic_cast(node->GetData()) != nullptr; + }); + + const mitk::Image* referenceImage = nullptr; + + for (auto node : result) + { + auto sources = dataStorage->GetSources(node, mitk::TNodePredicateDataType::New()); + + if (sources->size() != 1) + mitkThrow(); + + if (referenceImage == nullptr) + { + referenceImage = static_cast(sources->front()->GetData()); + } + else if (referenceImage != sources->front()->GetData()) + { + mitkThrow(); + } + } + + return result; + } +} + +QmitkConvertGeometryDataToROIAction::QmitkConvertGeometryDataToROIAction() +{ +} + +QmitkConvertGeometryDataToROIAction::~QmitkConvertGeometryDataToROIAction() +{ +} + +void QmitkConvertGeometryDataToROIAction::Run(const QList& selectedNodes) +{ + try + { + auto nodes = filterNodeSelection(selectedNodes, m_DataStorage); + } + catch (const mitk::Exception&) + { + handleInvalidNodeSelection(); + } +} + +void QmitkConvertGeometryDataToROIAction::SetDataStorage(mitk::DataStorage* dataStorage) +{ + m_DataStorage = dataStorage; +} + +void QmitkConvertGeometryDataToROIAction::SetFunctionality(berry::QtViewPart*) +{ +} + +void QmitkConvertGeometryDataToROIAction::SetSmoothed(bool) +{ +} + +void QmitkConvertGeometryDataToROIAction::SetDecimated(bool) +{ +} diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.h b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.h new file mode 100644 index 00000000000..8797841a765 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.h @@ -0,0 +1,37 @@ +/*============================================================================ + +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 QmitkConvertGeometryDataToROIAction_h +#define QmitkConvertGeometryDataToROIAction_h + +#include + +class QmitkConvertGeometryDataToROIAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + QmitkConvertGeometryDataToROIAction(); + ~QmitkConvertGeometryDataToROIAction() override; + + void Run(const QList& selectedNodes) override; + void SetDataStorage(mitk::DataStorage* dataStorage) override; + void SetFunctionality(berry::QtViewPart* functionality) override; + void SetSmoothed(bool smoothed) override; + void SetDecimated(bool decimated) override; + +private: + mitk::DataStorage::Pointer m_DataStorage; +}; + +#endif diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp index 0902061f1c7..291f8f18492 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp @@ -14,11 +14,13 @@ found in the LICENSE file. #include "mitkPluginActivator.h" #include "QmitkImageCropperView.h" +#include "QmitkConvertGeometryDataToROIAction.h" void mitk::mitkPluginActivator::start(ctkPluginContext* context) { RegisterBoundingShapeObjectFactory(); BERRY_REGISTER_EXTENSION_CLASS(QmitkImageCropperView, context) + BERRY_REGISTER_EXTENSION_CLASS(QmitkConvertGeometryDataToROIAction, context) } void mitk::mitkPluginActivator::stop(ctkPluginContext*) From f1cec65c81b125535e2410f57f89d31125ccf5db Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Sun, 12 Nov 2023 11:01:06 +0100 Subject: [PATCH 64/92] Add const overload of GetVtkMatrix() --- Modules/Core/include/mitkBaseGeometry.h | 1 + Modules/Core/src/DataManagement/mitkBaseGeometry.cpp | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/Modules/Core/include/mitkBaseGeometry.h b/Modules/Core/include/mitkBaseGeometry.h index 4d00557d8dc..6dff77d92a6 100644 --- a/Modules/Core/include/mitkBaseGeometry.h +++ b/Modules/Core/include/mitkBaseGeometry.h @@ -203,6 +203,7 @@ namespace mitk //## Get the Vtk Matrix which describes the transform. vtkMatrix4x4 *GetVtkMatrix(); + const vtkMatrix4x4* GetVtkMatrix() const; //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index 6a3a8352e7a..183e402aad3 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -694,6 +694,11 @@ vtkMatrix4x4 *mitk::BaseGeometry::GetVtkMatrix() return m_GeometryTransform->GetVtkMatrix(); } +const vtkMatrix4x4* mitk::BaseGeometry::GetVtkMatrix() const +{ + return m_GeometryTransform->GetVtkMatrix(); +} + bool mitk::BaseGeometry::IsBoundingBoxNull() const { return m_BoundingBox.IsNull(); From 3bbbbec20282932ca5fc7359f497edfaef458014 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Sun, 12 Nov 2023 11:02:16 +0100 Subject: [PATCH 65/92] Only save name if set --- Modules/ROI/autoload/IO/src/mitkROIIO.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp index 9cbc5fc05e4..ade9c8016d0 100644 --- a/Modules/ROI/autoload/IO/src/mitkROIIO.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -184,11 +184,14 @@ void mitk::ROIIO::Write() nlohmann::ordered_json j = { { "FileFormat", "MITK ROI" }, - { "Version", 1 }, - { "Name", input->GetProperty("name")->GetValueAsString() }, - { "Geometry", WriteGeometry(input->GetTimeGeometry()) } + { "Version", 1 } }; + if (auto name = input->GetProperty("name"); name.IsNotNull()) + j["Name"] = name->GetValueAsString(); + + j["Geometry"] = WriteGeometry(input->GetTimeGeometry()); + auto caption = input->GetConstProperty("caption"); if (caption.IsNotNull()) From 526f0f0bca858ae2c3e9c9e7e5861e94fa94f8da Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Sun, 12 Nov 2023 11:03:09 +0100 Subject: [PATCH 66/92] Add conversion from GeometryData to ROI --- .../QmitkConvertGeometryDataToROIAction.cpp | 86 ++++++++++++++++--- 1 file changed, 72 insertions(+), 14 deletions(-) diff --git a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp index 50fcc3a3e11..5dd30205468 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp @@ -15,6 +15,7 @@ found in the LICENSE file. #include #include #include +#include #include @@ -22,33 +23,51 @@ namespace { void handleInvalidNodeSelection() { - auto message = QStringLiteral("All selected bounding boxes must be child nodes of a single common reference image!"); + auto message = QStringLiteral("All selected bounding boxes must be child nodes of a single common reference image with a non-rotated geometry!"); MITK_ERROR << message; QMessageBox::warning(nullptr, QStringLiteral("Convert to ROI"), message); } - std::vector filterNodeSelection(const QList& selectedNodes, const mitk::DataStorage* dataStorage) + bool isRotated(const mitk::BaseGeometry* geometry) { - std::vector result; + const auto* matrix = geometry->GetVtkMatrix(); - std::copy_if(selectedNodes.cbegin(), selectedNodes.cend(), std::back_inserter(result), [](const mitk::DataNode* node) { - return node != nullptr && dynamic_cast(node->GetData()) != nullptr; - }); + for (int j = 0; j < 3; ++j) + { + for (int i = 0; i < 3; ++i) + { + if (i != j && matrix->GetElement(i, j) > mitk::eps) + return true; + } + } - const mitk::Image* referenceImage = nullptr; + return false; + } - for (auto node : result) + std::pair, mitk::DataNode*> getValidInput(const QList& selectedNodes, const mitk::DataStorage* dataStorage) + { + std::pair, mitk::DataNode*> result; + result.first.reserve(selectedNodes.size()); + + std::copy_if(selectedNodes.cbegin(), selectedNodes.cend(), std::back_inserter(result.first), [](const mitk::DataNode* node) { + return node != nullptr && dynamic_cast(node->GetData()) != nullptr; + }); + + for (auto node : result.first) { - auto sources = dataStorage->GetSources(node, mitk::TNodePredicateDataType::New()); + auto sourceNodes = dataStorage->GetSources(node, mitk::TNodePredicateDataType::New()); - if (sources->size() != 1) + if (sourceNodes->size() != 1) mitkThrow(); - if (referenceImage == nullptr) + if (result.second == nullptr) { - referenceImage = static_cast(sources->front()->GetData()); + if (isRotated(sourceNodes->front()->GetData()->GetGeometry())) + mitkThrow(); + + result.second = sourceNodes->front(); } - else if (referenceImage != sources->front()->GetData()) + else if (result.second != sourceNodes->front()) { mitkThrow(); } @@ -70,7 +89,46 @@ void QmitkConvertGeometryDataToROIAction::Run(const QListSetClonedGeometry(referenceNode->GetData()->GetGeometry()); + + unsigned int id = 0; + + for (auto node : nodes) + { + mitk::ROI::Element element(id++); + element.SetProperty("name", mitk::StringProperty::New(node->GetName())); + + if (auto* color = node->GetProperty("Bounding Shape.Deselected Color"); color != nullptr) + element.SetProperty("color", color); + + const auto* geometry = node->GetData()->GetGeometry(); + const auto origin = geometry->GetOrigin() - roi->GetGeometry()->GetOrigin(); + const auto spacing = geometry->GetSpacing(); + const auto bounds = geometry->GetBounds(); + + mitk::Point3D min; + mitk::Point3D max; + + for (size_t i = 0; i < 3; ++i) + { + min[i] = origin[i] / spacing[i] + bounds[2 * i]; + max[i] = origin[i] / spacing[i] + bounds[2 * i + 1] - 1; + } + + element.SetMin(min); + element.SetMax(max); + + roi->AddElement(element); + } + + auto roiNode = mitk::DataNode::New(); + roiNode->SetName(referenceNode->GetName() + " ROI" + (roi->GetNumberOfElements() > 1 ? "s" : "")); + roiNode->SetData(roi); + + m_DataStorage->Add(roiNode, referenceNode); } catch (const mitk::Exception&) { From 6ab482816510f736fd9d905b7430e0a34dc529c3 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Mon, 13 Nov 2023 13:55:35 +0100 Subject: [PATCH 67/92] Add MITK ROI file format documentation --- .../3-DeveloperManual/Concepts/Concepts.dox | 2 +- .../3-DeveloperManual/Concepts/MITKROIs.md | 144 +++++++++++++++++- Modules/ROI/include/mitkROI.h | 2 +- 3 files changed, 139 insertions(+), 9 deletions(-) diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox index 2213eef822e..c66a8971dc1 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox @@ -14,7 +14,7 @@ The following items describe some issues about MITK on a more abstract level. -# \subpage ReaderWriterPage -# \subpage MitkImagePage -# \subpage MITKSegmentationTaskListsPage - -# \subpage MITKROIsPage + -# \subpage MITKROIPage -# \subpage PropertiesPage -# \subpage GeometryOverviewPage -# \subpage PipelineingConceptPage diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md index 7e73aa67e88..2dac2c9742e 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md @@ -1,24 +1,154 @@ -# MITK ROIs {#MITKROIsPage} +# MITK ROI {#MITKROIPage} [TOC] +## Disclaimer + +Until the MITK ROI file format is going to be officially announced in a 2024 release of MITK, the file format must be considered experimental and is prone to change without any prior warning. + ## Overview -MITK ROIs are a JSON-based file format defining a list of region of interests (ROIs). +MITK ROI is a JSON-based file format defining a collection of region of interests (ROIs). + +ROIs must have an ID (unsigned integer) and their shape is currently considered to be an axis-aligned bounding box. +Its bounds are defined by minimum and maximum index coordinates, typically relative to an image. +Custom properties of various known types can be optionally attached to a ROI. +A few of these properties are used by MITK to define the appearance of a rendered ROI, for example: + + - "color" (mitk::ColorProperty): Color/RGB triplet of the rendered ROI (default: white \[1.0, 1.0, 1.0\]) + - "opacity" (mitk::FloatProperty): Opacity of the rendered ROI (default: 100% \[1.0\]) + - "lineWidth" (mitk::FloatProperty): Line width of the egdes of the rendered ROI (default: 1px \[1.0\]) + +ROIs can be optionally time-resolved and define both coordinates and properties per time step, allowing for a dynamic appearance, position, and size over time. -MITK ROIs must be considered experimental at the moment and are prone to change without any prior warning. +ROIs also display a caption at their bottom-left corner (supporting multiple lines), that can be set once per MITK ROI file for all contained ROIs. +Placeholders enclosed by braces in the caption are substituted by their corresponding ROI property values at runtime. +The default caption is "{name} ({ID})", where ``{ID}`` is a special placeholder for the ID of a ROI (technically not a ROI property), and ``{name}`` refers to the ROI property "name" (typically an mitk::StringProperty). + +Last but not least the reference (image) geometry of the ROIs in an MITK ROI file must be specified to be able to map all index coordinates to actual world coordinates. +A geometry is defined by an origin, the pixel/voxel spacing, a size, and optionally the number of time steps in case of a time-resolved MITK ROI file. ## File format -MITK ROIs are JSON files containing a JSON object as root, which must contain the two mandatory properties `FileFormat` and `Version`: +As all features are explained in the overview above, the JSON-based file format is defined here by two examples with minimal additional notes: one example for a static MITK ROI file and one example for a time-resolved MITK ROI file. + +### Static MITK ROI file + +This example contains two ROIs for detected tumors in an image with certain confidence. +Names and confidence values will be displayed in separate lines for each ROI. + +~~~{.json} +{ + "FileFormat": "MITK ROI", + "Version": 1, + "Name": "Static example", + "Caption": "{name}\nConfidence: {confidence}", + "Geometry": { + "Origin": [0, 0, 0], + "Spacing": [1, 1, 3], + "Size": [256, 256, 49] + }, + "ROIs": [ + { + "ID": 0, + "Min": [4, 4, 1], + "Max": [124, 124, 31], + "Properties": { + "StringProperty": { + "name": "tumor", + "comment": "Detected a tumor with 95% confidence.", + "note": "Properties are grouped by their type to reduce verbosity." + }, + "ColorProperty": { + "color": [0, 1, 0] + }, + "FloatProperty": { + "confidence": 0.95 + } + } + }, + { + "ID": 1, + "Min": [132, 4, 1], + "Max": [252, 60, 15], + "Properties": { + "StringProperty": { + "name": "Another tumor", + "comment": "Maybe another tumor (confidence only 25%)." + }, + "ColorProperty": { + "color": [1, 0, 0] + }, + "FloatProperty": { + "confidence": 0.25 + } + } + } + ] +} + +~~~ + +Further hints: + + - "FileFormat" ("MITK ROI"), "Version" (1), and "Geometry" are mandatory. + - "Name" is optional. If not set, the file name is used by MITK instead. + - ROIs are defined by JSON objects in the "ROIs" JSON array. + - See the derived classes of mitk::BaseProperty for an overview of known property types. + +### Time-resolved MITK ROI file + +This example only contains a single ROI but it is defined for several time steps. +Fallbacks of time step properties to default properties are demonstrated as well. ~~~{.json} { "FileFormat": "MITK ROI", - "Version": 1 + "Version": 1, + "Name": "Time-resolved example", + "Geometry": { + "Origin": [0, 0, 0], + "Spacing": [1, 1, 3], + "Size": [256, 256, 49], + "TimeSteps": 3 + }, + "ROIs": [ + { + "ID": 0, + "Properties": { + "ColorProperty": { + "color": [1, 0, 0] + }, + "StringProperty": { + "name": "Color-changing ROI" + } + }, + "TimeSteps": [ + { + "t": 0, + "Min": [4, 4, 1], + "Max": [124, 124, 31] + }, + { + "t": 2, + "Min": [14, 14, 11], + "Max": [121, 121, 28], + "Properties": { + "ColorProperty": { + "color": [0, 1, 0] + } + } + } + ] + } + ] } ~~~ -### ROIs +Further hints: -... + - The geometry defines 3 time steps. + - The "Properties" directly in the ROI function as fallbacks, if they are not defined for a certain time step. + - Time time step indices "t" are mandatory. The ROI is only present at time steps 0 and 2. + - The ROI is red (fallback) at time step 0 and green at time step 2. + - Its extents are larger at time step 0 than at time step 2. diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h index 5546f6100ff..ab1ee0464c3 100644 --- a/Modules/ROI/include/mitkROI.h +++ b/Modules/ROI/include/mitkROI.h @@ -51,7 +51,7 @@ namespace mitk * - \c font.bold (BoolProperty) Bold font style * - \c font.italic (BoolProperty) Italic font style * - * See \ref MITKROIsPage for details on the JSON-based MITK %ROI file format. + * See \ref MITKROIPage for details on the JSON-based MITK %ROI file format. */ class MITKROI_EXPORT ROI : public BaseData { From 01f785485a6b5a08fb8059dacc6422f4a4353f79 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Mon, 13 Nov 2023 21:03:50 +0100 Subject: [PATCH 68/92] Warn against use of experimental ROI class. --- Modules/ROI/include/mitkROI.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h index ab1ee0464c3..f8295e43b4d 100644 --- a/Modules/ROI/include/mitkROI.h +++ b/Modules/ROI/include/mitkROI.h @@ -19,6 +19,8 @@ found in the LICENSE file. namespace mitk { /** \brief A collection of region of interests (ROIs). + * + * \note This class is considered experimental and subject to substational change. We mean it. * * ROIs, essentially defined by the minimum and maximum index coordinates of an axis-aligned box, are * represented by the nested ROI::Element class. These index coordinates are relative to a common From e60d3c62a4e00b85366e56055052028a859a00c6 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Mon, 13 Nov 2023 21:04:37 +0100 Subject: [PATCH 69/92] Fix missing returns --- Modules/ROI/src/mitkROI.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp index 1dfd1ad3739..c9a07ead46f 100644 --- a/Modules/ROI/src/mitkROI.cpp +++ b/Modules/ROI/src/mitkROI.cpp @@ -271,11 +271,11 @@ void mitk::ROI::Element::SetProperty(const std::string& propertyKey, BasePropert if (it != m_Properties.end() && it->second.IsNotNull()) { it->second->SetProperty(propertyKey, property); + return; } - else if (!fallBackOnDefaultContext) - { + + if (!fallBackOnDefaultContext) mitkThrow() << "Context \"" << contextName << "\" does not exist!"; - } } m_DefaultProperties->SetProperty(propertyKey, property); @@ -291,11 +291,11 @@ void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, const st if (it != m_Properties.end() && it->second.IsNotNull()) { it->second->RemoveProperty(propertyKey); + return; } - else if (!fallBackOnDefaultContext) - { + + if (!fallBackOnDefaultContext) mitkThrow() << "Context \"" << contextName << "\" does not exist!"; - } } m_DefaultProperties->RemoveProperty(propertyKey); From b54bcd101ae7eabae4ed3b5eba9c207a55ba379a Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Mon, 13 Nov 2023 21:30:56 +0100 Subject: [PATCH 70/92] Add TimeStepType overloads to property methods --- Modules/ROI/include/mitkROI.h | 5 + Modules/ROI/src/mitkROI.cpp | 139 ++++++++++++++---------- Modules/ROI/src/mitkROIMapperHelper.cpp | 12 +- Modules/ROI/src/mitkROIMapperHelper.h | 8 +- 4 files changed, 94 insertions(+), 70 deletions(-) diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h index f8295e43b4d..25fb2ad7bc3 100644 --- a/Modules/ROI/include/mitkROI.h +++ b/Modules/ROI/include/mitkROI.h @@ -78,6 +78,7 @@ namespace mitk * An empty context name addresses the default properties. */ BaseProperty::ConstPointer GetConstProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = true) const override; + BaseProperty::ConstPointer GetConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext = true) const; /** \brief Get all property keys. * @@ -85,6 +86,7 @@ namespace mitk * An empty context name addresses the default properties. */ std::vector GetPropertyKeys(const std::string& contextName = "", bool includeDefaultContext = false) const override; + std::vector GetPropertyKeys(TimeStepType t, bool includeDefaultContext = false) const; /** \brief Get all property context names (stringified time steps). */ @@ -96,6 +98,7 @@ namespace mitk * An empty context name addresses the default properties. */ BaseProperty* GetNonConstProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = true) override; + BaseProperty* GetNonConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext = true); /** \brief Set a property. * @@ -103,6 +106,7 @@ namespace mitk * An empty context name addresses the default properties. */ void SetProperty(const std::string& propertyKey, BaseProperty* property, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; + void SetProperty(const std::string& propertyKey, BaseProperty* property, TimeStepType t, bool fallBackOnDefaultContext = false); /** \brief Remove a property. * @@ -110,6 +114,7 @@ namespace mitk * An empty context name addresses the default properties. */ void RemoveProperty(const std::string& propertyKey, const std::string& contextName = "", bool fallBackOnDefaultContext = false) override; + void RemoveProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext = false); unsigned int GetID() const; void SetID(unsigned int id); diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp index c9a07ead46f..6f5e614b1af 100644 --- a/Modules/ROI/src/mitkROI.cpp +++ b/Modules/ROI/src/mitkROI.cpp @@ -179,52 +179,56 @@ void mitk::ROI::Element::SetProperties(PropertyList* properties, TimeStepType t) mitk::BaseProperty::ConstPointer mitk::ROI::Element::GetConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) const { - if (!contextName.empty()) - { - const TimeStepType t = std::stoul(contextName); - auto it = m_Properties.find(t); + return !contextName.empty() + ? this->GetConstProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext) + : m_DefaultProperties->GetConstProperty(propertyKey); +} - if (it != m_Properties.end() && it->second.IsNotNull()) - { - auto property = it->second->GetConstProperty(propertyKey); +mitk::BaseProperty::ConstPointer mitk::ROI::Element::GetConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext) const +{ + auto it = m_Properties.find(t); - if (property.IsNotNull()) - return property; - } + if (it != m_Properties.end() && it->second.IsNotNull()) + { + auto property = it->second->GetConstProperty(propertyKey); - if (!fallBackOnDefaultContext) - return nullptr; + if (property.IsNotNull()) + return property; } + if (!fallBackOnDefaultContext) + return nullptr; + return m_DefaultProperties->GetConstProperty(propertyKey); } std::vector mitk::ROI::Element::GetPropertyKeys(const std::string& contextName, bool includeDefaultContext) const { - if (!contextName.empty()) - { - const TimeStepType t = std::stoul(contextName); - auto it = m_Properties.find(t); + return !contextName.empty() + ? this->GetPropertyKeys(std::stoul(contextName), includeDefaultContext) + : m_DefaultProperties->GetPropertyKeys(); +} - std::vector result; +std::vector mitk::ROI::Element::GetPropertyKeys(TimeStepType t, bool includeDefaultContext) const +{ + auto it = m_Properties.find(t); - if (it != m_Properties.end() && it->second.IsNotNull()) - result = it->second->GetPropertyKeys(); + std::vector result; - if (includeDefaultContext) - { - auto keys = m_DefaultProperties->GetPropertyKeys(); - auto end = result.cend(); + if (it != m_Properties.end() && it->second.IsNotNull()) + result = it->second->GetPropertyKeys(); - std::remove_copy_if(keys.cbegin(), keys.cend(), std::back_inserter(result), [&, result, end](const std::string& key) { - return end != std::find(result.cbegin(), end, key); - }); - } + if (includeDefaultContext) + { + auto keys = m_DefaultProperties->GetPropertyKeys(); + auto end = result.cend(); - return result; + std::remove_copy_if(keys.cbegin(), keys.cend(), std::back_inserter(result), [&, result, end](const std::string& key) { + return end != std::find(result.cbegin(), end, key); + }); } - return m_DefaultProperties->GetPropertyKeys(); + return result; } std::vector mitk::ROI::Element::GetPropertyContextNames() const @@ -241,23 +245,26 @@ std::vector mitk::ROI::Element::GetPropertyContextNames() const mitk::BaseProperty* mitk::ROI::Element::GetNonConstProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) { - if (!contextName.empty()) - { - const TimeStepType t = std::stoul(contextName); - auto it = m_Properties.find(t); + return !contextName.empty() + ? this->GetNonConstProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext) + : m_DefaultProperties->GetNonConstProperty(propertyKey); +} - if (it != m_Properties.end() && it->second.IsNotNull()) - { - auto* property = it->second->GetNonConstProperty(propertyKey); +mitk::BaseProperty* mitk::ROI::Element::GetNonConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext) +{ + auto it = m_Properties.find(t); - if (property != nullptr) - return property; - } + if (it != m_Properties.end() && it->second.IsNotNull()) + { + auto* property = it->second->GetNonConstProperty(propertyKey); - if (!fallBackOnDefaultContext) - return nullptr; + if (property != nullptr) + return property; } + if (!fallBackOnDefaultContext) + return nullptr; + return m_DefaultProperties->GetNonConstProperty(propertyKey); } @@ -265,19 +272,26 @@ void mitk::ROI::Element::SetProperty(const std::string& propertyKey, BasePropert { if (!contextName.empty()) { - const TimeStepType t = std::stoul(contextName); - auto it = m_Properties.find(t); + this->SetProperty(propertyKey, property, std::stoul(contextName), fallBackOnDefaultContext); + return; + } - if (it != m_Properties.end() && it->second.IsNotNull()) - { - it->second->SetProperty(propertyKey, property); - return; - } + m_DefaultProperties->SetProperty(propertyKey, property); +} - if (!fallBackOnDefaultContext) - mitkThrow() << "Context \"" << contextName << "\" does not exist!"; +void mitk::ROI::Element::SetProperty(const std::string& propertyKey, BaseProperty* property, TimeStepType t, bool fallBackOnDefaultContext) +{ + auto it = m_Properties.find(t); + + if (it != m_Properties.end() && it->second.IsNotNull()) + { + it->second->SetProperty(propertyKey, property); + return; } + if (!fallBackOnDefaultContext) + mitkThrow() << "Time step " << t << " does not exist!"; + m_DefaultProperties->SetProperty(propertyKey, property); } @@ -285,19 +299,26 @@ void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, const st { if (!contextName.empty()) { - const TimeStepType t = std::stoul(contextName); - auto it = m_Properties.find(t); + this->RemoveProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext); + return; + } - if (it != m_Properties.end() && it->second.IsNotNull()) - { - it->second->RemoveProperty(propertyKey); - return; - } + m_DefaultProperties->RemoveProperty(propertyKey); +} + +void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext) +{ + auto it = m_Properties.find(t); - if (!fallBackOnDefaultContext) - mitkThrow() << "Context \"" << contextName << "\" does not exist!"; + if (it != m_Properties.end() && it->second.IsNotNull()) + { + it->second->RemoveProperty(propertyKey); + return; } + if (!fallBackOnDefaultContext) + mitkThrow() << "Time step " << t << " does not exist!"; + m_DefaultProperties->RemoveProperty(propertyKey); } diff --git a/Modules/ROI/src/mitkROIMapperHelper.cpp b/Modules/ROI/src/mitkROIMapperHelper.cpp index 685d0957940..68dc54c545d 100644 --- a/Modules/ROI/src/mitkROIMapperHelper.cpp +++ b/Modules/ROI/src/mitkROIMapperHelper.cpp @@ -19,28 +19,26 @@ found in the LICENSE file. #include -void mitk::ROIMapperHelper::ApplyIndividualProperties(const IPropertyProvider& properties, TimeStepType t, vtkActor* actor) +void mitk::ROIMapperHelper::ApplyIndividualProperties(const ROI::Element& roi, TimeStepType t, vtkActor* actor) { auto* property = actor->GetProperty(); property->SetRepresentationToWireframe(); property->LightingOff(); - const auto contextName = std::to_string(t); - - if (auto colorProperty = GetConstProperty("color", properties, contextName); colorProperty != nullptr) + if (auto colorProperty = GetConstProperty("color", roi, t); colorProperty != nullptr) { const auto color = colorProperty->GetColor(); property->SetColor(color[0], color[1], color[2]); } - if (auto opacityProperty = GetConstProperty("opacity", properties, contextName); opacityProperty != nullptr) + if (auto opacityProperty = GetConstProperty("opacity", roi, t); opacityProperty != nullptr) { const auto opacity = opacityProperty->GetValue(); property->SetOpacity(property->GetOpacity() * opacity); } - if (auto lineWidthProperty = GetConstProperty("lineWidth", properties, contextName); lineWidthProperty != nullptr) + if (auto lineWidthProperty = GetConstProperty("lineWidth", roi, t); lineWidthProperty != nullptr) { const auto lineWidth = lineWidthProperty->GetValue(); property->SetLineWidth(lineWidth); @@ -101,7 +99,7 @@ std::string mitk::ROIMapperHelper::ParseCaption(const std::string& captionTempla } else { - auto property = roi.GetConstProperty(match[1], std::to_string(t)); + auto property = roi.GetConstProperty(match[1], t); if (property.IsNotNull()) caption.append(property->GetValueAsString()); diff --git a/Modules/ROI/src/mitkROIMapperHelper.h b/Modules/ROI/src/mitkROIMapperHelper.h index a529cc5b991..edab2ad05d4 100644 --- a/Modules/ROI/src/mitkROIMapperHelper.h +++ b/Modules/ROI/src/mitkROIMapperHelper.h @@ -26,7 +26,7 @@ namespace mitk { /** \brief Apply %ROI properties at a certain time step to the given actor. */ - void ApplyIndividualProperties(const IPropertyProvider& properties, TimeStepType t, vtkActor* actor); + void ApplyIndividualProperties(const ROI::Element& roi, TimeStepType t, vtkActor* actor); /** \brief Create an actor for the %ROI caption located at a certain attachment point considering several properties. */ @@ -42,12 +42,12 @@ namespace mitk */ void SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override); - /** \brief Syntactic sugar for getting properties. + /** \brief Syntactic sugar for getting %ROI properties. */ template - const T* GetConstProperty(const std::string& propertyKey, const mitk::IPropertyProvider& properties, const std::string& contextName = "") + const T* GetConstProperty(const std::string& propertyKey, const ROI::Element& roi, TimeStepType t) { - auto property = properties.GetConstProperty(propertyKey, contextName); + auto property = roi.GetConstProperty(propertyKey, t); if (property.IsNotNull()) return dynamic_cast(property.GetPointer()); From adce35032fe360abde8c4ff3f74b35e6b88ed482 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 15 Nov 2023 13:43:54 +0100 Subject: [PATCH 71/92] Disable deprecation warnings triggered by QList Necessary for Visual Studio 2022 version 17.8. --- Modules/QtWidgetsExt/CMakeLists.txt | 12 ++++++++++++ Plugins/org.blueberry.ui.qt/CMakeLists.txt | 12 ++++++++++++ 2 files changed, 24 insertions(+) diff --git a/Modules/QtWidgetsExt/CMakeLists.txt b/Modules/QtWidgetsExt/CMakeLists.txt index 85fdc156a10..9609183729c 100644 --- a/Modules/QtWidgetsExt/CMakeLists.txt +++ b/Modules/QtWidgetsExt/CMakeLists.txt @@ -4,3 +4,15 @@ MITK_CREATE_MODULE( PUBLIC Qwt CTK|CTKWidgets PRIVATE Qt5|Concurrent+Svg+Xml VTK|IOImage ) + +if (TARGET ${MODULE_TARGET} AND MSVC) + #[[ Compiler warnings/errors because of QList, resp. Qwt on Visual Studio 2022 version 17.8: + + 'stdext::checked_array_iterator': warning STL4043: stdext::checked_array_iterator, + stdext::unchecked_array_iterator, and related factory functions are non-Standard extensions + and will be removed in the future. std::span (since C++20) and gsl::span can be used instead. + You can define _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING or _SILENCE_ALL_MS_EXT_DEPRECATION_WARNINGS + to suppress this warning. + ]] + target_compile_definitions(${MODULE_TARGET} PUBLIC _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING) +endif() diff --git a/Plugins/org.blueberry.ui.qt/CMakeLists.txt b/Plugins/org.blueberry.ui.qt/CMakeLists.txt index 362c65471a3..b675068ba72 100644 --- a/Plugins/org.blueberry.ui.qt/CMakeLists.txt +++ b/Plugins/org.blueberry.ui.qt/CMakeLists.txt @@ -28,3 +28,15 @@ mitk_create_plugin( MODULE_DEPENDS PUBLIC MitkCore PACKAGE_DEPENDS ${PLUGIN_package_depends} ) + +if (TARGET ${PLUGIN_TARGET} AND MSVC) + #[[ Compiler warnings/errors because of QList on Visual Studio 2022 version 17.8: + + 'stdext::checked_array_iterator': warning STL4043: stdext::checked_array_iterator, + stdext::unchecked_array_iterator, and related factory functions are non-Standard extensions + and will be removed in the future. std::span (since C++20) and gsl::span can be used instead. + You can define _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING or _SILENCE_ALL_MS_EXT_DEPRECATION_WARNINGS + to suppress this warning. + ]] + target_compile_definitions(${PLUGIN_TARGET} PRIVATE _SILENCE_STDEXT_ARR_ITERS_DEPRECATION_WARNING) +endif() From 9787dadafd245ff0960026d29f3a3b085e406a7f Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 16 Nov 2023 10:15:07 +0100 Subject: [PATCH 72/92] Make messages for group deletion more explicit and clear --- .../Qmitk/QmitkMultiLabelInspector.cpp | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp index 6311220ef5e..2b2002ec073 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp @@ -586,6 +586,10 @@ void QmitkMultiLabelInspector::RemoveGroupInternal(const mitk::LabelSetImage::Gr mitkThrow() << "Segmentation or QmitkMultiLabelTreeModel is in an invalid state. Label is not present in the model after adding it to the segmentation. Label value: " << newLabelValue; } } + else + { + this->SetSelectedLabels({}); + } emit ModelUpdated(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); @@ -605,15 +609,19 @@ void QmitkMultiLabelInspector::RemoveGroup() return; } - auto question = QStringLiteral("Do you really want to delete the group of the selected label with all labels?"); - auto answer = QMessageBox::question(this, "Delete group", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + const auto* selectedLabel = this->GetFirstSelectedLabelObject(); - if (answer != QMessageBox::Yes) + if (selectedLabel == nullptr) return; - auto selectedLabel = this->GetFirstSelectedLabelObject(); const auto group = m_Segmentation->GetGroupIndexOfLabel(selectedLabel->GetValue()); + auto question = QStringLiteral("Do you really want to delete group %1 including all of its labels?").arg(group); + auto answer = QMessageBox::question(this, QStringLiteral("Delete group %1").arg(group), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); + + if (answer != QMessageBox::Yes) + return; + this->RemoveGroupInternal(group); } @@ -632,8 +640,8 @@ void QmitkMultiLabelInspector::OnDeleteGroup() { auto groupID = groupIDVariant.value(); - auto question = QStringLiteral("Do you really want to delete the current group with all labels?"); - auto answer = QMessageBox::question(this, QString("Delete group %1").arg(groupID), question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); + auto question = QStringLiteral("Do you really want to delete group %1 including all of its labels?").arg(groupID); + auto answer = QMessageBox::question(this, QString("Delete group %1").arg(groupID), question, QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (answer != QMessageBox::Yes) return; From 615a558597e55768603be58cbc552c89067ebe9d Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 16 Nov 2023 10:17:00 +0100 Subject: [PATCH 73/92] Find any label instance as closest label instance as last resort This edge case is provoked when groups have empty groups as siblings. --- .../SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp index 315e7952fb2..650634c667f 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp @@ -549,7 +549,14 @@ QModelIndex QmitkMultiLabelTreeModel::ClosestLabelInstanceIndex(const QModelInde } if (nullptr == resultItem) - return QModelIndex(); + { + // Try to find any other instance as a last resort... + resultItem = GetFirstInstanceLikeItem(rootItem); + + // ...as long as it is not itself. + if (currentItem == resultItem || nullptr == resultItem) + return QModelIndex(); + } return GetIndexByItem(resultItem, this); } From 1c8399354fa4941be4aa932001ba245bfd90172d Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 16 Nov 2023 10:49:34 +0100 Subject: [PATCH 74/92] Do not select label of group that is about to get removed --- Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp index 2b2002ec073..3911f06850b 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp @@ -554,6 +554,9 @@ void QmitkMultiLabelInspector::RemoveGroupInternal(const mitk::LabelSetImage::Gr auto nextIndex = m_Model->ClosestLabelInstanceIndex(currentIndex); auto labelVariant = nextIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole); + if (labelVariant.isValid() && m_Segmentation->GetGroupIndexOfLabel(labelVariant.value()) == groupID) + labelVariant = QVariant(); // Invalidate, since found label is child of group that is about to get removed + try { this->WaitCursorOn(); From aea0d7c5990603e76b65925d5db953c1553e8a5d Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Thu, 16 Nov 2023 15:15:20 +0100 Subject: [PATCH 75/92] Change search algorithm to include all nodes per level --- .../Qmitk/QmitkMultiLabelInspector.cpp | 3 -- .../Qmitk/QmitkMultiLabelTreeModel.cpp | 43 +++++++++++++------ 2 files changed, 29 insertions(+), 17 deletions(-) diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp index 3911f06850b..2b2002ec073 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelInspector.cpp @@ -554,9 +554,6 @@ void QmitkMultiLabelInspector::RemoveGroupInternal(const mitk::LabelSetImage::Gr auto nextIndex = m_Model->ClosestLabelInstanceIndex(currentIndex); auto labelVariant = nextIndex.data(QmitkMultiLabelTreeModel::ItemModelRole::LabelInstanceValueRole); - if (labelVariant.isValid() && m_Segmentation->GetGroupIndexOfLabel(labelVariant.value()) == groupID) - labelVariant = QVariant(); // Invalidate, since found label is child of group that is about to get removed - try { this->WaitCursorOn(); diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp index 650634c667f..bbf9702b835 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelTreeModel.cpp @@ -537,26 +537,41 @@ QModelIndex QmitkMultiLabelTreeModel::ClosestLabelInstanceIndex(const QModelInde while (searchItem != rootItem) { - resultItem = GetFirstInstanceLikeItem(searchItem->NextSibblingItem()); - if (nullptr != resultItem) break; + const auto* sibling = searchItem; - //no next closest label instance on this level -> check for closest before - resultItem = GetFirstInstanceLikeItem(searchItem->PrevSibblingItem()); - if (nullptr != resultItem) break; + while (sibling != nullptr) + { + sibling = sibling->NextSibblingItem(); + resultItem = GetFirstInstanceLikeItem(sibling); + + if (nullptr != resultItem) + break; + } + + if (nullptr != resultItem) + break; + + // No next closest label instance on this level -> check for closest before + sibling = searchItem; + + while (sibling != nullptr) + { + sibling = sibling->PrevSibblingItem(); + resultItem = GetFirstInstanceLikeItem(sibling); - //no closest label instance before current on this level -> moeve one level up + if (nullptr != resultItem) + break; + } + + if (nullptr != resultItem) + break; + + // No closest label instance before current on this level -> moeve one level up searchItem = searchItem->ParentItem(); } if (nullptr == resultItem) - { - // Try to find any other instance as a last resort... - resultItem = GetFirstInstanceLikeItem(rootItem); - - // ...as long as it is not itself. - if (currentItem == resultItem || nullptr == resultItem) - return QModelIndex(); - } + return QModelIndex(); return GetIndexByItem(resultItem, this); } From 36d33e1cab35e1a8540e253a1c9e4967f8c9d73e Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Mon, 20 Nov 2023 11:58:37 +0100 Subject: [PATCH 76/92] Correctly determine the closest point to a certain position --- .../mitkPointSetDataInteractor.cpp | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp b/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp index cfecd857448..f0930679109 100644 --- a/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp +++ b/Modules/Core/src/Interactions/mitkPointSetDataInteractor.cpp @@ -497,7 +497,9 @@ void mitk::PointSetDataInteractor::SetMaxPoints(unsigned int maxNumber) int mitk::PointSetDataInteractor::GetPointIndexByPosition(Point3D position, unsigned int time, float accuracy) { - // iterate over point set and check if it contains a point close enough to the pointer to be selected + // Iterate over point set and check if any point is close enough to the pointer to be selected. + // Choose the closest one of these candidates. + auto *points = dynamic_cast(GetDataNode()->GetData()); int index = -1; if (points == nullptr) @@ -510,16 +512,19 @@ int mitk::PointSetDataInteractor::GetPointIndexByPosition(Point3D position, unsi PointSet::PointsContainer *pointsContainer = points->GetPointSet(time)->GetPoints(); - float minDistance = m_SelectionAccuracy; - if (accuracy != -1) - minDistance = accuracy; + if (accuracy == -1) + accuracy = m_SelectionAccuracy; + + const auto end = pointsContainer->End(); + float minDistance = accuracy; + float distance; - for (PointSet::PointsIterator it = pointsContainer->Begin(); it != pointsContainer->End(); it++) + for (auto it = pointsContainer->Begin(); it != end; ++it) { - float distance = sqrt(position.SquaredEuclideanDistanceTo(points->GetPoint(it->Index(), time))); - if (distance < - minDistance) // if several points fall within the margin, choose the one with minimal distance to position + distance = sqrtf(position.SquaredEuclideanDistanceTo(points->GetPoint(it->Index(), time))); + if (distance < minDistance) { + minDistance = distance; index = it->Index(); } } From 6e9ca9ab0d34eaeecb74e5197233fa208ba44f6d Mon Sep 17 00:00:00 2001 From: Stephen Schaumann Date: Mon, 20 Nov 2023 16:34:04 +0100 Subject: [PATCH 77/92] check if control points are within image geometry --- .../src/internal/QmitkImageStatisticsView.cpp | 36 +++++++++++++------ .../src/internal/QmitkImageStatisticsView.h | 1 + 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index 5fa5a9da063..c89b13ff1b6 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -406,11 +406,9 @@ QmitkNodeSelectionDialog::SelectionCheckFunctionType QmitkImageStatisticsView::C } else { - const mitk::PlanarFigure* planar2 = dynamic_cast(rightNode->GetData()); - if (planar2) - { - validGeometry = mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planar2->GetPlaneGeometry(), imageNodeData->GetGeometry()); - } + const mitk::PlanarFigure* planarFigure = dynamic_cast(rightNode->GetData()); + const auto imageGeometry = imageNodeData->GetGeometry(); + validGeometry = CheckPlanarFigureMatchesGeometry(planarFigure, imageGeometry); } if (!validGeometry) @@ -452,11 +450,8 @@ mitk::NodePredicateBase::Pointer QmitkImageStatisticsView::GenerateROIPredicate( } else { - const auto planar2 = dynamic_cast(node->GetData()); - if (planar2) - { - sameGeometry = mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planar2->GetPlaneGeometry(), image->GetGeometry()); - } + const auto planarFigure = dynamic_cast(node->GetData()); + sameGeometry = CheckPlanarFigureMatchesGeometry(planarFigure, image->GetGeometry()); } return sameGeometry; @@ -467,3 +462,24 @@ mitk::NodePredicateBase::Pointer QmitkImageStatisticsView::GenerateROIPredicate( return result; } + +bool QmitkImageStatisticsView::CheckPlanarFigureMatchesGeometry(const mitk::PlanarFigure* planarFigure, const mitk::BaseGeometry* imageGeometry) +{ + if (!planarFigure || !imageGeometry) + { + return false; + } + + bool matchesGeometry = mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planarFigure->GetPlaneGeometry(), imageGeometry); + + for (int i = 0; i < planarFigure->GetNumberOfControlPoints(); ++i) + { + const auto controlPoint = planarFigure->GetWorldControlPoint(i); + if (!imageGeometry->IsInside(controlPoint)) + { + matchesGeometry = false; + } + } + + return matchesGeometry; +} diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h index 525466e3c05..35a76d1e227 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h @@ -82,6 +82,7 @@ class QmitkImageStatisticsView : public QmitkAbstractView, public mitk::IRenderW Ui::QmitkImageStatisticsViewControls m_Controls; private: + static bool CheckPlanarFigureMatchesGeometry(const mitk::PlanarFigure* planarFigure, const mitk::BaseGeometry* imageGeometry); QmitkNodeSelectionDialog::SelectionCheckFunctionType CheckForSameGeometry() const; mitk::NodePredicateBase::Pointer GenerateROIPredicate() const; From f90375061024318fc51e68ab45233fc5692ab8f8 Mon Sep 17 00:00:00 2001 From: Stephen Schaumann Date: Mon, 20 Nov 2023 17:25:36 +0100 Subject: [PATCH 78/92] fix sign of loop variable --- .../src/internal/QmitkImageStatisticsView.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index c89b13ff1b6..bc458f3b45b 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -472,7 +472,7 @@ bool QmitkImageStatisticsView::CheckPlanarFigureMatchesGeometry(const mitk::Plan bool matchesGeometry = mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planarFigure->GetPlaneGeometry(), imageGeometry); - for (int i = 0; i < planarFigure->GetNumberOfControlPoints(); ++i) + for (unsigned int i = 0; i < planarFigure->GetNumberOfControlPoints(); ++i) { const auto controlPoint = planarFigure->GetWorldControlPoint(i); if (!imageGeometry->IsInside(controlPoint)) From 1e02a70d45fb5d564b35f9adeadedeb39f4a8ceb Mon Sep 17 00:00:00 2001 From: Ashis Ravindran Date: Tue, 21 Nov 2023 20:11:00 +0100 Subject: [PATCH 79/92] Add dimension check for 2d images --- .../Interactions/mitkSegmentAnythingTool.cpp | 11 ++++++++++- .../Interactions/mitkSegmentAnythingTool.h | 2 +- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp index 94b63e83204..a94b7dacab9 100644 --- a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp @@ -257,7 +257,7 @@ void mitk::SegmentAnythingTool::DoUpdatePreview(const Image *inputAtTimeStep, LabelSetImage *previewImage, TimeStepType timeStep) { - if (nullptr != previewImage && m_PointSetPositive.IsNotNull()) + if (nullptr != previewImage && m_PointSetPositive.IsNotNull() && this->IsImageAtTimeStepValid(inputAtTimeStep)) { if (this->HasPicks() && nullptr != m_PythonService) { @@ -309,6 +309,15 @@ void mitk::SegmentAnythingTool::DoUpdatePreview(const Image *inputAtTimeStep, } } +bool mitk::SegmentAnythingTool::IsImageAtTimeStepValid(const Image *inputAtTimeStep) const +{ + int total = 0; + total += (inputAtTimeStep->GetDimension(0) > 1); + total += (inputAtTimeStep->GetDimension(1) > 1); + total += (inputAtTimeStep->GetDimension(2) > 1); + return (total > 1); +} + std::string mitk::SegmentAnythingTool::GetHashForCurrentPlane(const mitk::LevelWindow &levelWindow) { mitk::Vector3D normal = this->GetWorkingPlaneGeometry()->GetNormal(); diff --git a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h index e096072cc20..29858c2b2eb 100644 --- a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h +++ b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h @@ -203,7 +203,7 @@ namespace mitk * @param inputAtTimeStep * @return bool */ - bool IsImageAtTimeStepValid(const Image *inputAtTimeStep); + bool IsImageAtTimeStepValid(const Image *inputAtTimeStep) const; std::string m_MitkTempDir; std::string m_PythonPath; From 0e6de730897466b5007536101c233bb9721e3229 Mon Sep 17 00:00:00 2001 From: Stephen Schaumann Date: Wed, 22 Nov 2023 13:52:24 +0100 Subject: [PATCH 80/92] use auto and make added method a free function with earlier returns --- .../src/internal/QmitkImageStatisticsView.cpp | 44 +++++++++---------- .../src/internal/QmitkImageStatisticsView.h | 1 - 2 files changed, 21 insertions(+), 24 deletions(-) diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index bc458f3b45b..37d712022cf 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -40,6 +40,26 @@ found in the LICENSE file. const std::string QmitkImageStatisticsView::VIEW_ID = "org.mitk.views.imagestatistics"; +namespace { + bool CheckPlanarFigureMatchesGeometry(const mitk::PlanarFigure* planarFigure, const mitk::BaseGeometry* imageGeometry) + { + if (!planarFigure || !imageGeometry) + return false; + + if (!mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planarFigure->GetPlaneGeometry(), imageGeometry)) + return false; + + const auto numControlPoints = planarFigure->GetNumberOfControlPoints(); + for (unsigned int i = 0; i < numControlPoints; ++i) + { + if (!imageGeometry->IsInside(planarFigure->GetWorldControlPoint(i))) + return false; + } + + return true; + } +} // unnamed namespace + QmitkImageStatisticsView::~QmitkImageStatisticsView() { } @@ -330,7 +350,6 @@ void QmitkImageStatisticsView::OnROISelectionChanged(QmitkAbstractNodeSelectionW this->UpdateIntensityProfile(); } - void QmitkImageStatisticsView::OnButtonSelectionPressed() { QmitkNodeSelectionDialog* dialog = new QmitkNodeSelectionDialog(nullptr, "Select input for the statistic","You may select images and ROIs to compute their statistic. ROIs may be segmentations or planar figures."); @@ -406,7 +425,7 @@ QmitkNodeSelectionDialog::SelectionCheckFunctionType QmitkImageStatisticsView::C } else { - const mitk::PlanarFigure* planarFigure = dynamic_cast(rightNode->GetData()); + const auto planarFigure = dynamic_cast(rightNode->GetData()); const auto imageGeometry = imageNodeData->GetGeometry(); validGeometry = CheckPlanarFigureMatchesGeometry(planarFigure, imageGeometry); } @@ -462,24 +481,3 @@ mitk::NodePredicateBase::Pointer QmitkImageStatisticsView::GenerateROIPredicate( return result; } - -bool QmitkImageStatisticsView::CheckPlanarFigureMatchesGeometry(const mitk::PlanarFigure* planarFigure, const mitk::BaseGeometry* imageGeometry) -{ - if (!planarFigure || !imageGeometry) - { - return false; - } - - bool matchesGeometry = mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planarFigure->GetPlaneGeometry(), imageGeometry); - - for (unsigned int i = 0; i < planarFigure->GetNumberOfControlPoints(); ++i) - { - const auto controlPoint = planarFigure->GetWorldControlPoint(i); - if (!imageGeometry->IsInside(controlPoint)) - { - matchesGeometry = false; - } - } - - return matchesGeometry; -} diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h index 35a76d1e227..525466e3c05 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.h @@ -82,7 +82,6 @@ class QmitkImageStatisticsView : public QmitkAbstractView, public mitk::IRenderW Ui::QmitkImageStatisticsViewControls m_Controls; private: - static bool CheckPlanarFigureMatchesGeometry(const mitk::PlanarFigure* planarFigure, const mitk::BaseGeometry* imageGeometry); QmitkNodeSelectionDialog::SelectionCheckFunctionType CheckForSameGeometry() const; mitk::NodePredicateBase::Pointer GenerateROIPredicate() const; From fc8b96111fc67dd7d6a8e143df7878373f843b54 Mon Sep 17 00:00:00 2001 From: Ashis Ravindran Date: Thu, 23 Nov 2023 09:56:19 +0100 Subject: [PATCH 81/92] Convert IsImageAtTimeStepValid as free function --- .../Interactions/mitkSegmentAnythingTool.cpp | 25 +++++++++++-------- .../Interactions/mitkSegmentAnythingTool.h | 9 ------- 2 files changed, 15 insertions(+), 19 deletions(-) diff --git a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp index a94b7dacab9..967788e10a1 100644 --- a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.cpp @@ -252,12 +252,26 @@ void mitk::SegmentAnythingTool::ITKWindowing(const itk::ImageGetOutput()->GetPixelContainer()->ContainerManageMemoryOff(); } +namespace +{ + // Checks if the image has valid size across each dimension. The check is + // critical for 2D images since 2D image are not valid in Saggital and Coronal views. + bool IsImageAtTimeStepValid(const mitk::Image *inputAtTimeStep) + { + int total = 0; + total += (inputAtTimeStep->GetDimension(0) > 1); + total += (inputAtTimeStep->GetDimension(1) > 1); + total += (inputAtTimeStep->GetDimension(2) > 1); + return (total > 1); + } +} + void mitk::SegmentAnythingTool::DoUpdatePreview(const Image *inputAtTimeStep, const Image * /*oldSegAtTimeStep*/, LabelSetImage *previewImage, TimeStepType timeStep) { - if (nullptr != previewImage && m_PointSetPositive.IsNotNull() && this->IsImageAtTimeStepValid(inputAtTimeStep)) + if (nullptr != previewImage && m_PointSetPositive.IsNotNull() && ::IsImageAtTimeStepValid(inputAtTimeStep)) { if (this->HasPicks() && nullptr != m_PythonService) { @@ -309,15 +323,6 @@ void mitk::SegmentAnythingTool::DoUpdatePreview(const Image *inputAtTimeStep, } } -bool mitk::SegmentAnythingTool::IsImageAtTimeStepValid(const Image *inputAtTimeStep) const -{ - int total = 0; - total += (inputAtTimeStep->GetDimension(0) > 1); - total += (inputAtTimeStep->GetDimension(1) > 1); - total += (inputAtTimeStep->GetDimension(2) > 1); - return (total > 1); -} - std::string mitk::SegmentAnythingTool::GetHashForCurrentPlane(const mitk::LevelWindow &levelWindow) { mitk::Vector3D normal = this->GetWorkingPlaneGeometry()->GetNormal(); diff --git a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h index 29858c2b2eb..a200b2c09b0 100644 --- a/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h +++ b/Modules/Segmentation/Interactions/mitkSegmentAnythingTool.h @@ -196,15 +196,6 @@ namespace mitk */ static mitk::Point2D Get2DIndicesfrom3DWorld(const mitk::BaseGeometry*, const mitk::Point3D&); - /** - * @brief Checks if the image has valid size across each dimension. This function is - * critical for 2D images since 2D image are not valid in Saggital and Coronal views. - * - * @param inputAtTimeStep - * @return bool - */ - bool IsImageAtTimeStepValid(const Image *inputAtTimeStep) const; - std::string m_MitkTempDir; std::string m_PythonPath; std::string m_ModelType; From 0b3d5e72caf989b8a54e48222cb9591f9f1e42f0 Mon Sep 17 00:00:00 2001 From: Stephen Schaumann Date: Mon, 27 Nov 2023 11:13:34 +0100 Subject: [PATCH 82/92] allow selection of closed planar figures --- .../src/internal/QmitkImageStatisticsView.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index 37d712022cf..ad506c9ccdd 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -49,6 +49,9 @@ namespace { if (!mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planarFigure->GetPlaneGeometry(), imageGeometry)) return false; + if (planarFigure->IsClosed()) + return true; + const auto numControlPoints = planarFigure->GetNumberOfControlPoints(); for (unsigned int i = 0; i < numControlPoints; ++i) { From 1a88d6306e34514ee9365d7a21dad1dd2801c9f6 Mon Sep 17 00:00:00 2001 From: Stephen Schaumann Date: Mon, 27 Nov 2023 12:12:59 +0100 Subject: [PATCH 83/92] add comment to mark temporary code --- .../src/internal/QmitkImageStatisticsView.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp index ad506c9ccdd..3e2fc215354 100644 --- a/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp +++ b/Plugins/org.mitk.gui.qt.measurementtoolbox/src/internal/QmitkImageStatisticsView.cpp @@ -49,6 +49,7 @@ namespace { if (!mitk::PlanarFigureMaskGenerator::CheckPlanarFigureIsNotTilted(planarFigure->GetPlaneGeometry(), imageGeometry)) return false; + // The rest from here on is only needed until T30279 has been solved. if (planarFigure->IsClosed()) return true; From bdec20435e103a0028619c2e814a1d5c5d0d9df1 Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Mon, 27 Nov 2023 12:38:56 +0100 Subject: [PATCH 84/92] Appropriately compares two JSON values for equality --- .../include/mitkTestDICOMLoading.h | 3 +++ .../DICOMTesting/src/mitkTestDICOMLoading.cpp | 24 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/Modules/DICOMTesting/include/mitkTestDICOMLoading.h b/Modules/DICOMTesting/include/mitkTestDICOMLoading.h index dbbd4791132..5b96a0d4993 100644 --- a/Modules/DICOMTesting/include/mitkTestDICOMLoading.h +++ b/Modules/DICOMTesting/include/mitkTestDICOMLoading.h @@ -72,6 +72,9 @@ class MITKDICOMTESTING_EXPORT TestDICOMLoading const std::string& test, double eps = mitk::eps ); + bool CompareJSON( const std::string& reference, + const std::string& test ); + /** Compress whitespace in string \param pString input string diff --git a/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp b/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp index f20a80829bb..62c86a26142 100644 --- a/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp +++ b/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp @@ -431,6 +431,25 @@ mitk::TestDICOMLoading::CompareSpacedValueFields( const std::string& reference, return result; } + +bool +mitk::TestDICOMLoading::CompareJSON(const std::string& reference, const std::string& test) +{ + try + { + auto jReference = nlohmann::json::parse(reference); + auto jTest = nlohmann::json::parse(test); + + return jReference == jTest; + } + catch (const nlohmann::json::exception& e) + { + MITK_ERROR << e.what(); + return false; + } +} + + bool mitk::TestDICOMLoading::CompareImageInformationDumps( const std::string& referenceDump, const std::string& testDump ) @@ -465,6 +484,11 @@ mitk::TestDICOMLoading::CompareImageInformationDumps( const std::string& referen MITK_DEBUG << refKey << ": '" << gdcm::Version::GetVersion() << "' == '" << testValue << "' ? " << (thisTestResult ? "YES" : "NO"); } + else if (refKey == mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_FILES())) + { + bool thisTestResult = CompareJSON(refValue, testValue); + testResult &= thisTestResult; + } else { bool thisTestResult = CompareSpacedValueFields(refValue, testValue); From cf7f4a2ad5c57adba7bb6a05ff389e0b8d139abb Mon Sep 17 00:00:00 2001 From: floca Date: Tue, 28 Nov 2023 18:39:36 +0100 Subject: [PATCH 85/92] Updated icon of the view --- .../UserManual/inspector-dox.png | Bin 4125 -> 3988 bytes .../files.cmake | 2 +- .../org.mitk.gui.qt.dicominspector/plugin.xml | 5 +- .../resources/dicom_inspector.svg | 68 ++++++++++++++++++ .../resources/inspector.png | Bin 4125 -> 0 bytes 5 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 Plugins/org.mitk.gui.qt.dicominspector/resources/dicom_inspector.svg delete mode 100644 Plugins/org.mitk.gui.qt.dicominspector/resources/inspector.png diff --git a/Plugins/org.mitk.gui.qt.dicominspector/documentation/UserManual/inspector-dox.png b/Plugins/org.mitk.gui.qt.dicominspector/documentation/UserManual/inspector-dox.png index 6dc6b30c2982e70643acf6e887294f33859de2aa..1941802bab68f964a5f7453a273860aa5fe9004b 100644 GIT binary patch literal 3988 zcmZ`+XH*khlMXF(P->(I(gY%*_XyH^m#!!g2+{*cXab>!DnWxvSLsCpLg+041YSW< zi1c2BmnJGj%I5p_?CyK^?Ebjr%)RH#%ri63or|_KH(+MqVE_OC%tnT~*5oygJYwm9 z+wu--7z)w&y+x0CPf!$*2Da4G+1ey#JPs02nR=bqC$`{$xo)@;Hdo-ZP)&6yr()Y>ncUMZoDdGFyUZMyd=~D!(0`KnC5IuZdaI>c+a` z7*q5c!yOEVsK$rAqrZJLW%8_-N4$|CN!Zanc18J!-=4oUj|w7oTaYh{i;LOlC;?S? zD&jhdc!%y8h>n7d>c$QeTwYvBr*?k|Gp(T|q&i+w<;ES#5*y z?+Tbfk@bxYTwPt!raY#ra$#?P-2#f5pLYQ<0nPG6wk$;0dIkid)8yeJf`y~0E(6a^ z401ntKo9?t|JD(KG|Ly+GJ#$nP~U5htEi}`-(ZgFx;#Fm^275=cODBh`Oes}UM8!- za1JsmD#>ng=$4ii=8dTc>8Pay?$2a3%CzXy-uhcC{A%$%Jw53glb0(~@WMW)pa*b1g|uMmE0)0 zR}P9A8f=uy=+3|49wqH7GXi=ZS}Mcoo;qI6GyoQVg+B>O{LGy*EGoj`2=Mgu{5)|( zThjrXKv;4(=LMLGW!JJ?J*`(F={8{h8$A)AcFg|^?LQ7DwpYn`2DaRjioBsf+zF|2 z0ahG)WvuM4=la87Fzv=iy63nlAC>T*<&}MwDQ##*ZpgDM8ai%r*V>QfBPuoCIxvC3 z;7Wd>pYHDNG|*9SX91<*|Dii(UHu6&E9;}ni}O0C8YI6oSL(~WJmQRhSGtc9*t@K& zOCX6=ptL0VsQVD=0f!$#8s$#UzI9UbW0L1h>ydwi5?(f%C^S>QS z-YAI3h_?h{-ItF3Aodrvc6_}bn-11W8#mFBeX5)CacT};7@wRhZ80#qN?ZY+9& z%ooQUhLv$0CGV>JJLwcS<1mn-f{{vV*?9^bZjTtDxN1DX!t+ zJi@}lZz`D+v{jyLJ+I~`W3=10XRY^wZg_ZD1+g5z9-?TW_VjouNhR!uNS^t|{ehqs zKFr`dnEJ}fN^|Ie(5#(mN#^GxY!ag+9P~;8DAf9bAOZcB9 z@Q*lsI%GoK0c2{NZYEN47i=JYZQ17Cj<6f+Wyu_fqoQRYCz0pQGW@l5Mf}v1W1iX* zh6HUbo0-=s#T6Cw0~!46_xGnrq$C(FIcQ_VXxbG7Bnx#+Oj5DCbILw5P0U=WGy;+P zsL~L{9kKB6a5wvl-$zHo(+8nY5$bEFr>6?|iVD#4TNL(kqj#k5z9{4Jrz`?s{#~hL zGw;~QidWbhmRP6M7LO}GW<$rXD=Bgkh(u~~DD_6+4UTDPX&TE@dw9-x_Z5DX;4_0@NynKE|#8@&GIg_a6uC6 zD0>#Q?HlR49jVvf!j|eY785)c1097y9ub0vP}@ed9$$V2!k|A)q)fIiv9 zl$CPLw4heGmXT`l>H@>XIV1Oe`iN?Igx>^iw92o{Uf8A=VqqGj z8O$o!u}&G){FGAq$PA_TWWsDNLm{f&pQDRZo!@NAl&QIw-+vTm@A}Zo=4m6T%1;MV z1g2;!E^iTNO?6F=+fhC^Uz|uO?2Zvmh@Mi+2&wwHQ67TbT@hgKDZ))bcrQZ?*ukPgjMcvUDWMh*F(}x>DxMna?d*?+c^88 z?s`8|*wU)5Lue(Tms}P1Jry>_V4r$iEtOVI%%bVK(4h#~b?e~U!+-(2MbVT{m;7v_ z82DjD!3fiNzVY0X%&PJ#yX1KVk=GCmoBN3PVZi>j%%APb*sU z8y6?GJ3x*JC0xGT?k1Qa_d70(6kheg5xi-9ot;luG+(oPJS_#5APD9b zOkzz+IFq?n^sDFm??;1IEJpVF9PYM$Y#hkyw|8EPyOV?>;LpbAhR6w+17X5)_I5;m z7*EcLYU5Rw=Y5b>R}ja~$VnSuRC{(v+A%aq31G%Sgq+>17OPS{x30_ITDx^zWdW4Q z4m5N@x^3rG*XTHNFUq0ua$~Xd(wTe2loYY^kdYLDd-ECEMrV`XnDwB`Rn=-I>{)it zQT3Brrc602etJ?B9Ie#P9Oq*MTv&|N7B);_&T*mE2^4N|Ezupn0YYKF81KFp<=sD| zIY7c!!?>a%oEq-~UYbu2_dCQyKHOB|{Jbq5ZK3~m3uCwT*;nDJ@eCrQ#pnl5McJN~ zy>=i-H3zr!6Bg8WaZWnB>=F^RRak!eLjNqZl{Q+ol{b37N_wSCevWfW3f1%SBP;&B zl@n83fw)7~2K;X6wry-JyY1d6ehJ*^K6d!0{c1ac@Zk3>cL!3$x3GQA$(F%e%zXfC z|0h6nEav;Ww_9SNw;x%tUiY3qzWn_vXn*Dl9r|5Y9}%<0+{}i=-Yx^)@7sSoOLINV za~+W@PlKg6mMcSsy-Tu(b25EdJ+^#PRSv8Wd-`5kND_$iTZMfs34c??*D97Tr$^N* zaqvn3?5vw>X#W{})gnL?SbW!AKI7M-{t&9%a8^*s0@wGNbBC*OQz*vl87j}nBvaC< zhBw;?!cLoM1f2ny&xjgOh1*cda7iqv{r$UtXar?QljB64*)4WO$JlMQL%#)wzt@Bi zE%Obj<4PMC()`3il|-A*3wQ1#K9k^)c~!3A0#HJMRwYx1+`%PIG@?q%RMuvHI#5DX z4>OZN8t4{9G898WW^RALApTIWy5s2r$<)qY%taH?+1` zqbPn>P6Hdf$?n`c@+8|I_IQg-rh~e19v9?$ZC0mA!!I7I2DS$)Tc%1DITeZcJ(EQ6 zX0}>=75`7;oe~4XB3n*?Ww<6ih|^U}GVkvTh~KlySWND@ZR#$}6H{UCH0Z3v=lqv1 zw5h<);8gyK;u&N?aNL%f$1YC4f=#*R2c!2UB#~A`4h)}57D_=GwCzY z$v>r*NmYXvO+{4d`6$$>mGE;iU(e3FN>WJU2IOyu@q7wXFy;u>W$+jicq%srDAbFr z-@1Y1!`F}NNm|#5FXdibxhKYQ58dPNH7=_}Ebsn-5rdVKwb{9FC+8R&-U2p3lAcYi zl_C@wgb#&4gq8F;Bb!$|rdDn>yr$js zeYWu3j$SnR=p5F_7!#6hgM3=J&c!*tX9~j;D~n9WVzS&1mY=tznblt*5wZLEo!guz zO~dSj9QUDVb7`+54W`DovD9$lujfq%lttMfo;1FT^&T*Wl;`B5m6APj^K9c@+v>^t zW9)&!&RSuB>})AzCrMLcig)2J2ZWKUL>JrF6j$gL#CgR^>IpZ&9Lc0PrJQm-DU{h2 z1>bM5EUT`008xGJxKJs?XE(!#94RiP=^jb>wnEx8n$@3b%sOV09Mh1P#Nc!WlLE8PT0>qe? z1h6|5bqzY%o~EoFmG|Jlr~F9(C^fXwm%^Z}7c(&l2$1(CNc%Moot9#8<+Pc2amMgQ zbEO)qMo42}re%B%gRgpI zs%o^l>jah4>pZmDe7H3x8$q zx}AP5wcOX}t&*k(U9I&An^tegKW-$_mGL|u(a6yAVwVQw3R1P`$I36im)`9*rD;qh zJNE~yb!OdDSz&y#p!c9}?ZEh@*$jth!v}M%_i&r!r7z8l(Z6SUDusShjxHXr6|ML1 z*Exxv;|qa``8RGQVJ3_1$l(l9md$NistMqZLR9Hxf0bkfYQEa-KT*FPiT7u?&siJp z^hg!nUuDyATv#&1y;AO~u-tDix}50QBhq#Y$Fn7wc+0=LF`g>h=qC9&1!cA>MyH@A z$~I;;j+PIwdVIUh72)cI0n>?v(jS(H+POZpIwq?Hr#M63T@uehhB&x7Jk^G|u80neo)@nbD{SU}XcYpu@ literal 4125 zcmZ`+cR1Y7^M4;sJ3{o2=tPhsr^IQWa~#p4MnpL+y6C;TL!3A*h%S0BLG%`aNc0wx z5Kc+-nrMmgz0aS&KYqKjJI~HOvpdi1Yv!4Kp{JupOU+IV006Cqx{3jj!~PqTneV5prP-Ca!sG)D5%&AV?4ZFi`;ThbY2q0Dv!1amxw-L%U*0OtB{fF4207KlO$9}R6)3IaJfHJ9-AlB_%c z(9vk9C>c5~el`j8N%@g=9r`J~NkcQGarA+xD3hk98Ffk20H2XkIdd(`P4t0C;UNC? zp`1n6s^>pH7J@kk<-Kt)w@m)An-}HBsqO z;7*5xUpXn>$)_ga`$wJQf1~j4=sp3(zy!zxM0u6Flb|t^E2L3E9DCC*m!Q4q(Qtvx1Nn9aR}Awb#0{>$BRDBdfM$ z@q-s_Nn~`83_aCa-({ba9$DZA}#I0bhT?{4PQd`oPR4a$ouJW3!(97W(%L zX?0d6Bn*NojCYdXwR?R{MWmaB84wA|q|UPTEl_3&+5~Am}W`Zmy)% zdzLBba0Dh%QkipW;b)@O2x9^2jdRfV)IZK`H2x0W&QlBh?9^Ox)oK2b)tLG4(E6tK z-MIS>3;)Q7*LQ1rUjmZ_Pf)Gs2%0s-aHyg>@7A!sotenGo zv8#6y?hSwX!~*Ieda#Jy)`Uecg$j{JM@22Adknw-qUxAll5RejxxH?lwEoMy#;p~a+IyR1Wlw?x|sF* zD5{U6qnytMa7hLgWd5u6jg3tOj?3?FhUDrDy1KZ;QCX3>=qML1eD(cobn&IKs4sB( z=a}NzBh}}j2N_5nWLOv?Rr7}{m=2KAMk(lE_fK{chjg{InZ4W4uFzZjK$K8I+)<#Y zr-4a;jJttf!spdvzYcV52DTmwdHXdE$%Cm^&q~5bzXjjXecew)F7BYwv_?XRo=BCXlfOAK=4e5tt-DH!kQs2CZstgNo~ zHbCX%rG*SDa%n@h8VK|@0W#zsRp3LFa2Cu3!B|9fmcjo?5vf1mKev9J$Q9S7w zHEJKJSrVaB_TOw;6HNq{QPp!IsqiTU2g`>bxDkuNWX|HWyw=N2^ThinCMQ9}H!Lm| zn2?I^!_fr?DlCR)jM__^LMPpxa;xz;pEoz}wY}P|h`snA#J#h)H+drMuH z-r7>#3bJ39rl%!~nc&%@<<-?HT3Qe>bSow{mYPG>i;R<#6TmDkT4X#F92pv?(4G!( z|10)zR2}u?8H8ozejK%FjS)nKV=QX&OuuTf_QUY4SLDa?9m9#?dKXo8y0q^Y142Mr z`Z*ty8I*bGDXm~MwEf%1E5A3V%PT91pz5EZ-~vFsX$_&RQxh@cSI_u$!n!C@_S@uH z0IK1JilRfcU~ls2=Q*Q5N$B3IrAq|gLt_h-2<74{&H!$jJN3qKg@t_8LZSl=$qYod zC0?I!BEFg>Jh4RMs?u*e7Ra$?*du9pAY=nR>NxC>I=@G*8ezeHb7ArrNA~CnZy z+vB^%28bH{+k%JXSDO^W4I4Wfn9o6+I%N=0G$?V9RC}5ys%Q6@kX`95Gye_idsh>7 zx~ClS&Q6j`%pzi~SRkOwf=I?tma`B9j3)B@*E)*tB{5H7hJS4BkpW_cc?b@gGou=j z(4Fb5*@Oo+}_dboC32O+f>0LgGuX1%I;=1*&e~_(KeH0Hp&C|JpqA!QX zEmpm~ofirrVcpDTLw66+Sgt_0+m^9F$|nHnsRjUqsWxwBr8Lp5{oWt)c_!!Tv)pjk z%RcbWggE%_5Me9xm8{9RF<1G znDTd%2l}Y{f=`TA5h$gH*G#I|@%6+%jh!VQW?w;Aw-j{NQFB2f@=GC`=b0kk1O5=Y zNze8Snf-R;X+AiS4xb_M0t9Mebl#p=Dj84tJIzRnG4DKS5*4;%%d$+{~Vc9m_&0j}cA|Z8Ag-(m*4a<6zv<}x4bMvMLkDkF3 zMl5M*Qu2OZ3SM}gcV)ocxyb*hNN3YFH8l~u+KgL0cxT^OE}58_WpnL11yrdo(F2eAz0i`6n-es@lhI#rMWWBBCNLb8>#%4xxA!@AH$N0uI?JJYHMPfB8$j4KWLo^uZ``eL_%Zl%WV15D*mf5@66^z>H-83$2;+8rP@9(io#d zGDGr3kW>V8!1!v2AgQ}htq_Xfj_Bnnb{NBaYhx_000jjrdA4CpX>IMSos~dbDyg4n)wR4)uh7MZlal(_P~t=#=D1NXO}2WN5fC$e$F+!0|@6KUFh zfq^5*K_6Mz&uG?tL0<3)g&xJLDXsoZABM7NjrOaH^_bV*Lrg2MZ2rzkMB>F&sKgh` zUov1%(zU~>G7W!!f0Z%M10GM(;`Cu(6}*p3Evw%@591D+7p{c>tH4K$vlrW?>cUE)XS@KR(KJ*%Yn0c?2PUDB z$}|rVz=I$_9W{7j_8JFyl|?<|3Ql=7g{WxfNyAkQ@E6cd2!I*TcEHOijj49e~xt@q<9V{jAi5M)T7qdM}+^bZA{wL zui)FB13i=nd}hx}-QB3f;Ds%2qggHZ*jNeo;NiWBJK>^GUN?*j9Lg6#=al30y9ZQ<}W7xyjLarxfBz1=r;U{?~^l&5x zlPXJB_e&Y%O$ybUYUZ56Vq%E%w;&npAjR2-sHhW@xAt}b1|lN`+XFQBDKXZQemmv7 zxYD(oaAfV_SwC&k^{mc;V$;vf*#SK3lWQo@_cn<%z=yu>^UGJDt#;5&H*ECzs?DLz zpr*ySqq^TvxI0B}Xo;duuH0kac_4vwVH-0`RqY9?iHumd9?M5HQV?1(MUiJsHwahc zNH1`bDX5Flc$Hk_sZIvEc2(g#YqH#s5sn;^^rAeOVRU)J2ZMfNr|l^>ag^fg(c!KW zbNZ4uC&*&~3Q+Y%NGvzG$N$$NwY!y6)_dk47#m9p41g5GRu&I=@1w&E0l0Gk z4MeJSsLZ;x + icon="resources/dicom_inspector.svg" + category="Data"> + View DICOM information stored as properties in data instances. + diff --git a/Plugins/org.mitk.gui.qt.dicominspector/resources/dicom_inspector.svg b/Plugins/org.mitk.gui.qt.dicominspector/resources/dicom_inspector.svg new file mode 100644 index 00000000000..4c28b406f7e --- /dev/null +++ b/Plugins/org.mitk.gui.qt.dicominspector/resources/dicom_inspector.svg @@ -0,0 +1,68 @@ + + + + + + image/svg+xml + + + + + + + + DCM + diff --git a/Plugins/org.mitk.gui.qt.dicominspector/resources/inspector.png b/Plugins/org.mitk.gui.qt.dicominspector/resources/inspector.png deleted file mode 100644 index 6dc6b30c2982e70643acf6e887294f33859de2aa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4125 zcmZ`+cR1Y7^M4;sJ3{o2=tPhsr^IQWa~#p4MnpL+y6C;TL!3A*h%S0BLG%`aNc0wx z5Kc+-nrMmgz0aS&KYqKjJI~HOvpdi1Yv!4Kp{JupOU+IV006Cqx{3jj!~PqTneV5prP-Ca!sG)D5%&AV?4ZFi`;ThbY2q0Dv!1amxw-L%U*0OtB{fF4207KlO$9}R6)3IaJfHJ9-AlB_%c z(9vk9C>c5~el`j8N%@g=9r`J~NkcQGarA+xD3hk98Ffk20H2XkIdd(`P4t0C;UNC? zp`1n6s^>pH7J@kk<-Kt)w@m)An-}HBsqO z;7*5xUpXn>$)_ga`$wJQf1~j4=sp3(zy!zxM0u6Flb|t^E2L3E9DCC*m!Q4q(Qtvx1Nn9aR}Awb#0{>$BRDBdfM$ z@q-s_Nn~`83_aCa-({ba9$DZA}#I0bhT?{4PQd`oPR4a$ouJW3!(97W(%L zX?0d6Bn*NojCYdXwR?R{MWmaB84wA|q|UPTEl_3&+5~Am}W`Zmy)% zdzLBba0Dh%QkipW;b)@O2x9^2jdRfV)IZK`H2x0W&QlBh?9^Ox)oK2b)tLG4(E6tK z-MIS>3;)Q7*LQ1rUjmZ_Pf)Gs2%0s-aHyg>@7A!sotenGo zv8#6y?hSwX!~*Ieda#Jy)`Uecg$j{JM@22Adknw-qUxAll5RejxxH?lwEoMy#;p~a+IyR1Wlw?x|sF* zD5{U6qnytMa7hLgWd5u6jg3tOj?3?FhUDrDy1KZ;QCX3>=qML1eD(cobn&IKs4sB( z=a}NzBh}}j2N_5nWLOv?Rr7}{m=2KAMk(lE_fK{chjg{InZ4W4uFzZjK$K8I+)<#Y zr-4a;jJttf!spdvzYcV52DTmwdHXdE$%Cm^&q~5bzXjjXecew)F7BYwv_?XRo=BCXlfOAK=4e5tt-DH!kQs2CZstgNo~ zHbCX%rG*SDa%n@h8VK|@0W#zsRp3LFa2Cu3!B|9fmcjo?5vf1mKev9J$Q9S7w zHEJKJSrVaB_TOw;6HNq{QPp!IsqiTU2g`>bxDkuNWX|HWyw=N2^ThinCMQ9}H!Lm| zn2?I^!_fr?DlCR)jM__^LMPpxa;xz;pEoz}wY}P|h`snA#J#h)H+drMuH z-r7>#3bJ39rl%!~nc&%@<<-?HT3Qe>bSow{mYPG>i;R<#6TmDkT4X#F92pv?(4G!( z|10)zR2}u?8H8ozejK%FjS)nKV=QX&OuuTf_QUY4SLDa?9m9#?dKXo8y0q^Y142Mr z`Z*ty8I*bGDXm~MwEf%1E5A3V%PT91pz5EZ-~vFsX$_&RQxh@cSI_u$!n!C@_S@uH z0IK1JilRfcU~ls2=Q*Q5N$B3IrAq|gLt_h-2<74{&H!$jJN3qKg@t_8LZSl=$qYod zC0?I!BEFg>Jh4RMs?u*e7Ra$?*du9pAY=nR>NxC>I=@G*8ezeHb7ArrNA~CnZy z+vB^%28bH{+k%JXSDO^W4I4Wfn9o6+I%N=0G$?V9RC}5ys%Q6@kX`95Gye_idsh>7 zx~ClS&Q6j`%pzi~SRkOwf=I?tma`B9j3)B@*E)*tB{5H7hJS4BkpW_cc?b@gGou=j z(4Fb5*@Oo+}_dboC32O+f>0LgGuX1%I;=1*&e~_(KeH0Hp&C|JpqA!QX zEmpm~ofirrVcpDTLw66+Sgt_0+m^9F$|nHnsRjUqsWxwBr8Lp5{oWt)c_!!Tv)pjk z%RcbWggE%_5Me9xm8{9RF<1G znDTd%2l}Y{f=`TA5h$gH*G#I|@%6+%jh!VQW?w;Aw-j{NQFB2f@=GC`=b0kk1O5=Y zNze8Snf-R;X+AiS4xb_M0t9Mebl#p=Dj84tJIzRnG4DKS5*4;%%d$+{~Vc9m_&0j}cA|Z8Ag-(m*4a<6zv<}x4bMvMLkDkF3 zMl5M*Qu2OZ3SM}gcV)ocxyb*hNN3YFH8l~u+KgL0cxT^OE}58_WpnL11yrdo(F2eAz0i`6n-es@lhI#rMWWBBCNLb8>#%4xxA!@AH$N0uI?JJYHMPfB8$j4KWLo^uZ``eL_%Zl%WV15D*mf5@66^z>H-83$2;+8rP@9(io#d zGDGr3kW>V8!1!v2AgQ}htq_Xfj_Bnnb{NBaYhx_000jjrdA4CpX>IMSos~dbDyg4n)wR4)uh7MZlal(_P~t=#=D1NXO}2WN5fC$e$F+!0|@6KUFh zfq^5*K_6Mz&uG?tL0<3)g&xJLDXsoZABM7NjrOaH^_bV*Lrg2MZ2rzkMB>F&sKgh` zUov1%(zU~>G7W!!f0Z%M10GM(;`Cu(6}*p3Evw%@591D+7p{c>tH4K$vlrW?>cUE)XS@KR(KJ*%Yn0c?2PUDB z$}|rVz=I$_9W{7j_8JFyl|?<|3Ql=7g{WxfNyAkQ@E6cd2!I*TcEHOijj49e~xt@q<9V{jAi5M)T7qdM}+^bZA{wL zui)FB13i=nd}hx}-QB3f;Ds%2qggHZ*jNeo;NiWBJK>^GUN?*j9Lg6#=al30y9ZQ<}W7xyjLarxfBz1=r;U{?~^l&5x zlPXJB_e&Y%O$ybUYUZ56Vq%E%w;&npAjR2-sHhW@xAt}b1|lN`+XFQBDKXZQemmv7 zxYD(oaAfV_SwC&k^{mc;V$;vf*#SK3lWQo@_cn<%z=yu>^UGJDt#;5&H*ECzs?DLz zpr*ySqq^TvxI0B}Xo;duuH0kac_4vwVH-0`RqY9?iHumd9?M5HQV?1(MUiJsHwahc zNH1`bDX5Flc$Hk_sZIvEc2(g#YqH#s5sn;^^rAeOVRU)J2ZMfNr|l^>ag^fg(c!KW zbNZ4uC&*&~3Q+Y%NGvzG$N$$NwY!y6)_dk47#m9p41g5GRu&I=@1w&E0l0Gk z4MeJSsLZ;x Date: Tue, 28 Nov 2023 18:40:10 +0100 Subject: [PATCH 86/92] Optimized predicates to prohibit from selecting helper or hidden nodes. --- .../src/internal/QmitkDicomInspectorView.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp index c0808922fb3..f396482627a 100644 --- a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp +++ b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp @@ -20,6 +20,9 @@ found in the LICENSE file. #include #include #include +#include +#include +#include // Qt #include @@ -80,9 +83,14 @@ void QmitkDicomInspectorView::CreateQtPartControl(QWidget* parent) // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); + auto nodePredicate = mitk::NodePredicateAnd::New(); + nodePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); + nodePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); + m_Controls.singleSlot->SetDataStorage(GetDataStorage()); m_Controls.singleSlot->SetSelectionIsOptional(true); m_Controls.singleSlot->SetAutoSelectNewNodes(true); + m_Controls.singleSlot->SetNodePredicate(nodePredicate); m_Controls.singleSlot->SetEmptyInfo(QString("Please select a data node")); m_Controls.singleSlot->SetPopUpTitel(QString("Select data node")); From 2a8634dd0c031f88c7b072254de01a48de931999 Mon Sep 17 00:00:00 2001 From: floca Date: Tue, 28 Nov 2023 18:40:43 +0100 Subject: [PATCH 87/92] Activated dicominspector and DICOMCmdApps in the workbench config. --- CMake/BuildConfigurations/WorkbenchRelease.cmake | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMake/BuildConfigurations/WorkbenchRelease.cmake b/CMake/BuildConfigurations/WorkbenchRelease.cmake index 0163c6a085c..168a400d97f 100644 --- a/CMake/BuildConfigurations/WorkbenchRelease.cmake +++ b/CMake/BuildConfigurations/WorkbenchRelease.cmake @@ -13,12 +13,14 @@ set(MITK_CONFIG_PLUGINS ${MITK_CONFIG_PLUGINS} org.mitk.gui.qt.matchpoint.visualizer org.mitk.gui.qt.matchpoint.evaluator org.mitk.gui.qt.matchpoint.manipulator + org.mitk.gui.qt.dicominspector ) if(NOT MITK_USE_SUPERBUILD) set(BUILD_CoreCmdApps ON CACHE BOOL "" FORCE) set(BUILD_MatchPointCmdApps ON CACHE BOOL "" FORCE) set(BUILD_SegmentationCmdApps ON CACHE BOOL "" FORCE) + set(BUILD_DICOMCmdApps ON CACHE BOOL "" FORCE) endif() set(MITK_VTK_DEBUG_LEAKS OFF CACHE BOOL "Enable VTK Debug Leaks" FORCE) From 7533ca99fee5c7dc8c1a2b4d7ab391fa27573ff6 Mon Sep 17 00:00:00 2001 From: Timo <43245438+TheRisenPhoenix@users.noreply.github.com> Date: Wed, 22 Nov 2023 14:40:36 +0100 Subject: [PATCH 88/92] Migrate polhemus widget to new ITK --- Modules/IGTUI/Qmitk/QmitkPolhemusTrackerWidget.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/Modules/IGTUI/Qmitk/QmitkPolhemusTrackerWidget.cpp b/Modules/IGTUI/Qmitk/QmitkPolhemusTrackerWidget.cpp index 89b5715c1e2..77b20e3dae0 100644 --- a/Modules/IGTUI/Qmitk/QmitkPolhemusTrackerWidget.cpp +++ b/Modules/IGTUI/Qmitk/QmitkPolhemusTrackerWidget.cpp @@ -193,18 +193,20 @@ void QmitkPolhemusTrackerWidget::on_m_ToggleToolTipCalibration_clicked() if (m_Controls->m_ToolSelection->currentIndex() != 0) { mitk::PolhemusTool* _tool = dynamic_cast (this->m_TrackingDevice->GetToolByName(m_Controls->m_ToolSelection->currentText().toStdString())); - mitk::Point3D tip = _tool->GetToolTipPosition().GetVectorFromOrigin()*(-1.); + auto tip = _tool->GetToolTipPosition().GetVectorFromOrigin()*(-1.); + auto tipPoint = mitk::Point3D(tip); mitk::Quaternion quat = _tool->GetToolAxisOrientation().inverse(); - _tool->SetToolTipPosition(tip, quat); + _tool->SetToolTipPosition(tipPoint, quat); } else { for (int i = 0; i < m_TrackingDevice->GetToolCount(); ++i) { mitk::PolhemusTool* _tool = dynamic_cast (this->m_TrackingDevice->GetTool(i)); - mitk::Point3D tip = _tool->GetToolTipPosition().GetVectorFromOrigin()*(-1.); + auto tip = _tool->GetToolTipPosition().GetVectorFromOrigin()*(-1.); + auto tipPoint = mitk::Point3D(tip); mitk::Quaternion quat = _tool->GetToolAxisOrientation().inverse(); - _tool->SetToolTipPosition(tip, quat); + _tool->SetToolTipPosition(tipPoint, quat); } } } From ec90ae59c15e20597b8370a5d5c469a7291751e1 Mon Sep 17 00:00:00 2001 From: Timo <43245438+TheRisenPhoenix@users.noreply.github.com> Date: Fri, 24 Nov 2023 10:43:54 +0100 Subject: [PATCH 89/92] Remove Polhemus GetLastResultStr Fix syntax error --- .../IGT/TrackingDevices/mitkPolhemusInterface.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Modules/IGT/TrackingDevices/mitkPolhemusInterface.cpp b/Modules/IGT/TrackingDevices/mitkPolhemusInterface.cpp index 6ec9715e983..89c0b1e9cf1 100644 --- a/Modules/IGT/TrackingDevices/mitkPolhemusInterface.cpp +++ b/Modules/IGT/TrackingDevices/mitkPolhemusInterface.cpp @@ -40,7 +40,7 @@ bool mitk::PolhemusInterface::SetupDevice() m_pdiDev->SetPnoBuffer(MotionBuf, 0x1FA400); m_pdiDev->SetMetric(true); //use cm instead of inches - m_pdiDev->StartPipeExport(); + //m_pdiDev->StartPipeExport(); CPDImdat pdiMDat; pdiMDat.Empty(); @@ -94,13 +94,13 @@ bool mitk::PolhemusInterface::OpenConnection() switch (eType) { case PI_CNX_USB: - MITK_INFO << "USB Connection: " << m_pdiDev->GetLastResultStr(); + MITK_INFO << "USB Connection"; break; case PI_CNX_SERIAL: - MITK_INFO << "Serial Connection: " << m_pdiDev->GetLastResultStr(); + MITK_INFO << "Serial Connection"; break; default: - MITK_INFO << "DiscoverCnx result: " << m_pdiDev->GetLastResultStr(); + MITK_INFO << "DiscoverCnx"; break; } @@ -185,7 +185,7 @@ bool mitk::PolhemusInterface::Disconnect() } returnValue = m_pdiDev->Disconnect(); - MITK_INFO << "Disconnect: " << m_pdiDev->GetLastResultStr(); + MITK_INFO << "Disconnect"; return returnValue; } @@ -217,7 +217,7 @@ std::vector mitk::PolhemusInterface::GetL DWORD dwSize; //read one frame - if (!m_pdiDev->LastPnoPtr(pBuf, dwSize)) { MITK_WARN << m_pdiDev->GetLastResultStr(); } + if (!m_pdiDev->LastPnoPtr(pBuf, dwSize)) { MITK_WARN << "There is an issue"; } std::vector returnValue = ParsePolhemusRawData(pBuf, dwSize); @@ -241,7 +241,7 @@ std::vector mitk::PolhemusInterface::GetS //read one frame if (!m_pdiDev->ReadSinglePnoBuf(pBuf, dwSize)) { - MITK_WARN << m_pdiDev->GetLastResultStr(); + MITK_WARN << "There is an issue"; return std::vector(); } From 5028271e7099251243e4f8a85c9de84b70debafa Mon Sep 17 00:00:00 2001 From: Stefan Dinkelacker Date: Wed, 29 Nov 2023 15:59:01 +0100 Subject: [PATCH 90/92] Python version check for Totalsegmentator and SAM tool Summary: While installation of Totalsegmentator and SAM tool, user need to specify a system python. However, tool need modern versions of python to work. Until now, a version check was not imposed technically, but only via documentation since we don't create/maintain the python code. We only verified if there was a python executable for an installation to begin. This diff adds check to python version, as well. v3.8 < Python < v3.12 Test Plan: NA Reviewers: floca, O1 MITK Reviewer Group I, a178n Reviewed By: floca Differential Revision: https://phabricator.mitk.org/D888 --- .../Qmitk/QmitkSetupVirtualEnvUtil.cpp | 91 ++++++++++++++++++ .../Qmitk/QmitkSetupVirtualEnvUtil.h | 9 +- .../Qmitk/QmitkTotalSegmentatorToolGUI.cpp | 86 +++++++---------- .../Qmitk/QmitkTotalSegmentatorToolGUI.h | 10 +- .../QmitkSegmentAnythingPreferencePage.cpp | 94 ++++++++----------- .../src/QmitkSegmentAnythingPreferencePage.h | 10 +- .../src/QmitkSegmentAnythingPreferencePage.ui | 2 +- 7 files changed, 179 insertions(+), 123 deletions(-) diff --git a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp index 9fd02ade305..326bbc522fd 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.cpp @@ -15,6 +15,11 @@ found in the LICENSE file.s #include "mitkLog.h" #include #include +#include +#include +#include +#include +#include QmitkSetupVirtualEnvUtil::QmitkSetupVirtualEnvUtil() { @@ -181,3 +186,89 @@ bool QmitkSetupVirtualEnvUtil::IsPythonPath(const QString &pythonPath) #endif return isExists; } + +namespace +{ + std::mutex mutex; + std::string pyVersionCaptured; + + void CapturePyVersion(itk::Object * /*pCaller*/, const itk::EventObject &e, void *) + { + std::string testCOUT; + const auto *pEvent = dynamic_cast(&e); + if (pEvent) + { + pyVersionCaptured = pEvent->GetOutput(); + } + } + + std::vector SplitVersionString(const std::string& version) + { + std::vector splits; + std::string part; + std::istringstream tokenStream(version); + while (std::getline(tokenStream, part, '.')) + { + splits.push_back(std::stoi(part)); + } + return splits; + } + + bool IsSupported(const std::string& version, const std::string& low, const std::string& high) + { + std::vector inHandVersion = SplitVersionString(version); + std::vector targetLowVersion = SplitVersionString(low); + std::vector targetHighVersion = SplitVersionString(high); + if (inHandVersion.size() > 1 && targetLowVersion.size() > 1 && targetHighVersion.size() > 1) + { // comparing second part of the version + return (inHandVersion[1] > targetLowVersion[1] && inHandVersion[1] < targetHighVersion[1]); + } + return false; + } +} + +std::pair QmitkSetupVirtualEnvUtil::GetExactPythonPath(const QString &pyEnv) +{ + QString fullPath = pyEnv; + bool pythonDoesExist = false; + bool isSupportedVersion = false; +#ifdef _WIN32 + const std::string PYTHON_EXE = "python.exe"; + // check if python exist in given folder. + pythonDoesExist = QFile::exists(fullPath + QDir::separator() + QString::fromStdString(PYTHON_EXE)); + if (!pythonDoesExist && // check if in Scripts already, if not go there + !(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) + { + fullPath += QDir::separator() + QString("Scripts"); + pythonDoesExist = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); + } +#else + const std::string PYTHON_EXE = "python3"; + pythonDoesExist = QFile::exists(fullPath + QDir::separator() + QString::fromStdString(PYTHON_EXE)); + if (!pythonDoesExist && + !(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) + { + fullPath += QDir::separator() + QString("bin"); + pythonDoesExist = QFile::exists(fullPath + QDir::separator() + QString("python3")); + } +#endif + std::pair pythonPath; + if (pythonDoesExist) + { + ::mutex.lock(); + std::regex sanitizer(R"([^3\.(\d+)])"); + mitk::ProcessExecutor::ArgumentListType args; + auto spExec = mitk::ProcessExecutor::New(); + auto spCommand = itk::CStyleCommand::New(); + spCommand->SetCallback(&::CapturePyVersion); + spExec->AddObserver(mitk::ExternalProcessOutputEvent(), spCommand); + args.push_back("--version"); + spExec->Execute(fullPath.toStdString(), PYTHON_EXE, args); + std::string pyVersionNumber = std::regex_replace(::pyVersionCaptured, sanitizer, ""); + isSupportedVersion = ::IsSupported(pyVersionNumber, "3.8", "3.13"); + pythonPath.second = QString::fromStdString(pyVersionNumber); + ::mutex.unlock(); + } + pythonPath.first = pythonDoesExist &&isSupportedVersion ? fullPath : ""; + return pythonPath; +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h index 3f822b66dc8..518fc5addc4 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h +++ b/Modules/SegmentationUI/Qmitk/QmitkSetupVirtualEnvUtil.h @@ -17,8 +17,6 @@ found in the LICENSE file.s #include "mitkProcessExecutor.h" #include #include -#include -#include /** * @brief Abstract Class to Setup a python virtual environment and pip install required packages. @@ -185,6 +183,13 @@ class MITKSEGMENTATIONUI_EXPORT QmitkSetupVirtualEnvUtil */ static void PrintProcessEvent(itk::Object *, const itk::EventObject &e, void *); + /** + * @brief Get the exact Python path and version for any OS from the virtual environment path. + * @return A pair of the exact python path and its Python version or empty, if an supported + * version of Python could not be found. + */ + static std::pair GetExactPythonPath(const QString &pyEnv); + private: QString m_PythonPath; QString m_PipPath; diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp index 8b24a854dc2..a78bef468a9 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.cpp @@ -92,7 +92,7 @@ void QmitkTotalSegmentatorToolGUI::InitializeUI(QBoxLayout *mainLayout) m_IsInstalled = this->IsTotalSegmentatorInstalled(storageDir); if (m_IsInstalled) { - m_PythonPath = GetExactPythonPath(storageDir); + m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(storageDir).first; m_Installer.SetVirtualEnvPath(m_PythonPath); this->EnableAll(m_IsInstalled); welcomeText += " TotalSegmentator is already found installed."; @@ -153,25 +153,36 @@ void QmitkTotalSegmentatorToolGUI::EnableAll(bool isEnable) void QmitkTotalSegmentatorToolGUI::OnInstallBtnClicked() { bool isInstalled = false; - QString systemPython = OnSystemPythonChanged(m_Controls.sysPythonComboBox->currentText()); - if (systemPython.isEmpty()) + const auto [path, version] = OnSystemPythonChanged(m_Controls.sysPythonComboBox->currentText()); + if (path.isEmpty()) { - this->WriteErrorMessage("ERROR: Couldn't find Python."); + this->WriteErrorMessage("ERROR: Couldn't find compatible Python."); + return; + } + // check if python 3.12 and ask for confirmation + if (version.startsWith("3.12") && + QMessageBox::No == QMessageBox::question( + nullptr, + "Installing TotalSegmentator", + QString("WARNING: This is an unsupported version of Python that may not work. " + "We recommend using a supported Python version between 3.9 and 3.11.\n\n" + "Continue anyway?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No)) + { + return; + } + this->WriteStatusMessage("STATUS: Installing TotalSegmentator..."); + m_Installer.SetSystemPythonPath(path); + isInstalled = m_Installer.SetupVirtualEnv(m_Installer.VENV_NAME); + if (isInstalled) + { + m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(m_Installer.GetVirtualEnvPath()).first; + this->WriteStatusMessage("STATUS: Successfully installed TotalSegmentator."); } else { - this->WriteStatusMessage("STATUS: Installing TotalSegmentator..."); - m_Installer.SetSystemPythonPath(systemPython); - isInstalled = m_Installer.SetupVirtualEnv(m_Installer.VENV_NAME); - if (isInstalled) - { - m_PythonPath = this->GetExactPythonPath(m_Installer.GetVirtualEnvPath()); - this->WriteStatusMessage("STATUS: Successfully installed TotalSegmentator."); - } - else - { - this->WriteErrorMessage("ERROR: Couldn't install TotalSegmentator."); - } + this->WriteErrorMessage("ERROR: Couldn't install TotalSegmentator."); } this->EnableAll(isInstalled); } @@ -317,15 +328,15 @@ void QmitkTotalSegmentatorToolGUI::AutoParsePythonPaths() QString envName = subIt.fileName(); if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any. { - m_Controls.sysPythonComboBox->addItem("(" + envName + "): " + subIt.filePath()); + m_Controls.pythonEnvComboBox->addItem("(" + envName + "): " + subIt.filePath()); } } } } -QString QmitkTotalSegmentatorToolGUI::OnSystemPythonChanged(const QString &pyEnv) +std::pair QmitkTotalSegmentatorToolGUI::OnSystemPythonChanged(const QString &pyEnv) { - QString pyPath; + std::pair pyPath; if (pyEnv == QString("Select")) { m_Controls.previewButton->setDisabled(true); @@ -344,7 +355,7 @@ QString QmitkTotalSegmentatorToolGUI::OnSystemPythonChanged(const QString &pyEnv else { QString uiPyPath = this->GetPythonPathFromUI(pyEnv); - pyPath = this->GetExactPythonPath(uiPyPath); + pyPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(uiPyPath); } return pyPath; } @@ -366,7 +377,7 @@ void QmitkTotalSegmentatorToolGUI::OnPythonPathChanged(const QString &pyEnv) oldState); // unblock signal firing after inserting item. Remove this after Qt6 migration } } - else if (!this->IsTotalSegmentatorInstalled(pyEnv)) + else if (!this->IsTotalSegmentatorInstalled(this->GetPythonPathFromUI(pyEnv))) { this->ShowErrorMessage(WARNING_TOTALSEG_NOT_FOUND); m_Controls.previewButton->setDisabled(true); @@ -375,7 +386,7 @@ void QmitkTotalSegmentatorToolGUI::OnPythonPathChanged(const QString &pyEnv) {// Show positive status meeage m_Controls.previewButton->setDisabled(false); QString uiPyPath = this->GetPythonPathFromUI(pyEnv); - m_PythonPath = this->GetExactPythonPath(uiPyPath); + m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(uiPyPath).first; } } @@ -389,34 +400,6 @@ QString QmitkTotalSegmentatorToolGUI::GetPythonPathFromUI(const QString &pyUI) c return fullPath.simplified(); } -QString QmitkTotalSegmentatorToolGUI::GetExactPythonPath(const QString &pyEnv) const -{ - QString fullPath = pyEnv; - bool isPythonExists = false; -#ifdef _WIN32 - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); - if (!isPythonExists && - !(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) - { - fullPath += QDir::separator() + QString("Scripts"); - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); - } -#else - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); - if (!isPythonExists && !(fullPath.endsWith("bin", Qt::CaseInsensitive) || - fullPath.endsWith("bin/", Qt::CaseInsensitive))) - { - fullPath += QDir::separator() + QString("bin"); - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); - } -#endif - if (!isPythonExists) - { - fullPath.clear(); - } - return fullPath; -} - void QmitkTotalSegmentatorToolGUI::OnOverrideChecked(int state) { bool isEnabled = false; @@ -433,7 +416,8 @@ void QmitkTotalSegmentatorToolGUI::OnOverrideChecked(int state) if (m_IsInstalled) { const QString pythonPath = m_Installer.GetVirtualEnvPath(); - m_PythonPath = this->GetExactPythonPath(pythonPath); + auto pathObject = QmitkSetupVirtualEnvUtil::GetExactPythonPath(pythonPath); + m_PythonPath = pathObject.first; this->EnableAll(m_IsInstalled); } } diff --git a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h index fb1442dfaa3..ac0c787f4c8 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitkTotalSegmentatorToolGUI.h @@ -21,6 +21,7 @@ found in the LICENSE file. #include #include #include +#include /** * @brief Installer class for TotalSegmentator Tool. @@ -72,7 +73,7 @@ protected slots: /** * @brief Qt Slot */ - QString OnSystemPythonChanged(const QString &); + std::pair OnSystemPythonChanged(const QString &); /** * @brief Qt Slot @@ -151,13 +152,6 @@ protected slots: */ QString GetPythonPathFromUI(const QString &) const; - /** - * @brief Get the Exact Python Path for any OS - * from the virtual environment path. - * @return QString - */ - QString GetExactPythonPath(const QString &) const; - /** * @brief For storing values like Python path across sessions. */ diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.cpp index 3229b83a628..66bbb404e16 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.cpp @@ -46,6 +46,9 @@ void QmitkSegmentAnythingPreferencePage::CreateQtControl(QWidget* parent) { m_Control = new QWidget(parent); m_Ui->setupUi(m_Control); + + m_Ui->samModelTipLabel->hide(); // TODO: All models except for vit_b seem to be unsupported by SAM? + #ifndef _WIN32 m_Ui->sysPythonComboBox->addItem("/usr/bin"); #endif @@ -66,7 +69,7 @@ void QmitkSegmentAnythingPreferencePage::CreateQtControl(QWidget* parent) QString welcomeText; if (isInstalled) { - m_PythonPath = GetExactPythonPath(storageDir); + m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(storageDir).first; m_Installer.SetVirtualEnvPath(m_PythonPath); welcomeText += " Segment Anything tool is already found installed."; m_Ui->installSAMButton->setEnabled(false); @@ -124,9 +127,9 @@ void QmitkSegmentAnythingPreferencePage::Update() } } -QString QmitkSegmentAnythingPreferencePage::OnSystemPythonChanged(const QString &pyEnv) +std::pair QmitkSegmentAnythingPreferencePage::OnSystemPythonChanged(const QString &pyEnv) { - QString pyPath; + std::pair pyPath; if (pyEnv == QString("Select...")) { QString path = QFileDialog::getExistingDirectory(m_Ui->sysPythonComboBox->parentWidget(), "Python Path", "dir"); @@ -142,7 +145,7 @@ QString QmitkSegmentAnythingPreferencePage::OnSystemPythonChanged(const QString else { QString uiPyPath = this->GetPythonPathFromUI(pyEnv); - pyPath = this->GetExactPythonPath(uiPyPath); + pyPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(uiPyPath); } return pyPath; } @@ -157,34 +160,6 @@ QString QmitkSegmentAnythingPreferencePage::GetPythonPathFromUI(const QString &p return fullPath.simplified(); } -QString QmitkSegmentAnythingPreferencePage::GetExactPythonPath(const QString &pyEnv) const -{ - QString fullPath = pyEnv; - bool isPythonExists = false; -#ifdef _WIN32 - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); - if (!isPythonExists && - !(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) - { - fullPath += QDir::separator() + QString("Scripts"); - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python.exe")); - } -#else - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); - if (!isPythonExists && - !(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) - { - fullPath += QDir::separator() + QString("bin"); - isPythonExists = QFile::exists(fullPath + QDir::separator() + QString("python3")); - } -#endif - if (!isPythonExists) - { - fullPath.clear(); - } - return fullPath; -} - void QmitkSegmentAnythingPreferencePage::AutoParsePythonPaths() { QString homeDir = QDir::homePath(); @@ -258,28 +233,41 @@ int QmitkSegmentAnythingPreferencePage::FetchSelectedGPUFromUI() const void QmitkSegmentAnythingPreferencePage::OnInstallBtnClicked() { - QString systemPython = OnSystemPythonChanged(m_Ui->sysPythonComboBox->currentText()); - if (!systemPython.isEmpty()) + const auto [path, version] = OnSystemPythonChanged(m_Ui->sysPythonComboBox->currentText()); + if (path.isEmpty()) { - this->WriteStatusMessage("STATUS: Installing SAM..."); - m_Ui->installSAMButton->setEnabled(false); - m_Installer.SetSystemPythonPath(systemPython); - bool isInstalled = false; - bool isFinished = m_Installer.SetupVirtualEnv(m_Installer.VENV_NAME); - if (isFinished) - { - isInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(m_Installer.GetVirtualEnvPath()); - } - if (isInstalled) - { - m_PythonPath = this->GetExactPythonPath(m_Installer.GetVirtualEnvPath()); - this->WriteStatusMessage("STATUS: Successfully installed SAM."); - } - else - { - this->WriteErrorMessage("ERROR: Couldn't install SAM."); - m_Ui->installSAMButton->setEnabled(true); - } + this->WriteErrorMessage("ERROR: Couldn't find compatible Python."); + return; + } + //check if python 3.12 and ask for confirmation + if (version.startsWith("3.12") && + QMessageBox::No == QMessageBox::question(nullptr, + "Installing Segment Anything", + QString("WARNING: This is an unsupported version of Python that may not work. " + "We recommend using a supported Python version between 3.9 and 3.11.\n\n" + "Continue anyway?"), + QMessageBox::Yes | QMessageBox::No, + QMessageBox::No)) + { + return; + } + this->WriteStatusMessage("STATUS: Installing SAM..."); + m_Ui->installSAMButton->setEnabled(false); + m_Installer.SetSystemPythonPath(path); + bool isInstalled = false; + if (m_Installer.SetupVirtualEnv(m_Installer.VENV_NAME)) + { + isInstalled = QmitkSegmentAnythingToolGUI::IsSAMInstalled(m_Installer.GetVirtualEnvPath()); + } + if (isInstalled) + { + m_PythonPath = QmitkSetupVirtualEnvUtil::GetExactPythonPath(m_Installer.GetVirtualEnvPath()).first; + this->WriteStatusMessage("STATUS: Successfully installed SAM."); + } + else + { + this->WriteErrorMessage("ERROR: Couldn't install SAM."); + m_Ui->installSAMButton->setEnabled(true); } } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.h b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.h index 3a4118eaeca..cbb758eed57 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.h @@ -19,6 +19,7 @@ found in the LICENSE file. #include #include #include +#include class QWidget; @@ -61,7 +62,7 @@ class QmitkSegmentAnythingPreferencePage : public QObject, public berry::IQtPref private slots: void OnInstallBtnClicked(); void OnClearInstall(); - QString OnSystemPythonChanged(const QString&); + std::pair OnSystemPythonChanged(const QString &); protected: /** @@ -78,13 +79,6 @@ private slots: */ QString GetPythonPathFromUI(const QString &) const; - /** - * @brief Get the Exact Python Path for any OS - * from the virtual environment path. - * @return QString - */ - QString GetExactPythonPath(const QString &) const; - /** * @brief Adds GPU information to the gpu combo box. * In case, there aren't any GPUs avaialble, the combo box will be diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.ui b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.ui index b68da828a36..647fec49e68 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentAnythingPreferencePage.ui @@ -48,7 +48,7 @@ - System Python + System Python (v3.9 - v3.11) From 76a3107afedbcb9e36b6de58f16ee2dc7b1f9106 Mon Sep 17 00:00:00 2001 From: floca Date: Wed, 29 Nov 2023 16:29:49 +0100 Subject: [PATCH 91/92] - fixed T30287 - also fixed crash due to dangeling observers by refactoring the view to use QmitkSliceNavigationListener --- .../src/internal/QmitkDicomInspectorView.cpp | 155 ++++-------------- .../src/internal/QmitkDicomInspectorView.h | 40 +---- 2 files changed, 38 insertions(+), 157 deletions(-) diff --git a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp index f396482627a..c4508d80b39 100644 --- a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp +++ b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.cpp @@ -34,18 +34,36 @@ found in the LICENSE file. const std::string QmitkDicomInspectorView::VIEW_ID = "org.mitk.views.dicominspector"; -QmitkDicomInspectorView::ObserverInfo::ObserverInfo(mitk::SliceNavigationController* controller, - int observerTag, mitk::IRenderWindowPart* part) - : controller(controller) - , observerTag(observerTag) - , renderWindowPart(part) +namespace { + QmitkAbstractNodeSelectionWidget::NodeList GetInitialSelection(berry::ISelection::ConstPointer selection) + { + if (selection.IsNotNull() && !selection->IsEmpty()) + { + auto dataNodeSelection = dynamic_cast(selection.GetPointer()); + + if (nullptr != dataNodeSelection) + { + auto firstSelectedDataNode = dataNodeSelection->GetSelectedDataNodes().front(); + + if (firstSelectedDataNode.IsNotNull()) + { + QmitkAbstractNodeSelectionWidget::NodeList initialSelection; + initialSelection.push_back(firstSelectedDataNode); + + return initialSelection; + } + } + } + + return QmitkAbstractNodeSelectionWidget::NodeList(); + } } QmitkDicomInspectorView::QmitkDicomInspectorView() : m_RenderWindowPart(nullptr) - , m_PendingSliceChangedEvent(false) , m_SelectedNode(nullptr) + , m_ValidSelectedPosition(false) , m_SelectedTimePoint(0.) , m_CurrentSelectedZSlice(0) { @@ -54,7 +72,6 @@ QmitkDicomInspectorView::QmitkDicomInspectorView() QmitkDicomInspectorView::~QmitkDicomInspectorView() { - this->RemoveAllObservers(); } void QmitkDicomInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) @@ -63,19 +80,14 @@ void QmitkDicomInspectorView::RenderWindowPartActivated(mitk::IRenderWindowPart* { m_RenderWindowPart = renderWindowPart; - if (!InitObservers()) - { - QMessageBox::information(nullptr, "Error", "Unable to set up the event observers. The " \ - "plot will not be triggered on changing the crosshair, " \ - "position or time step."); - } + this->m_SliceNavigationListener.RenderWindowPartActivated(renderWindowPart); } } void QmitkDicomInspectorView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) { m_RenderWindowPart = nullptr; - this->RemoveAllObservers(renderWindowPart); + this->m_SliceNavigationListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkDicomInspectorView::CreateQtPartControl(QWidget* parent) @@ -89,7 +101,6 @@ void QmitkDicomInspectorView::CreateQtPartControl(QWidget* parent) m_Controls.singleSlot->SetDataStorage(GetDataStorage()); m_Controls.singleSlot->SetSelectionIsOptional(true); - m_Controls.singleSlot->SetAutoSelectNewNodes(true); m_Controls.singleSlot->SetNodePredicate(nodePredicate); m_Controls.singleSlot->SetEmptyInfo(QString("Please select a data node")); m_Controls.singleSlot->SetPopUpTitel(QString("Select data node")); @@ -105,78 +116,14 @@ void QmitkDicomInspectorView::CreateQtPartControl(QWidget* parent) mitk::IRenderWindowPart* renderWindowPart = GetRenderWindowPart(); RenderWindowPartActivated(renderWindowPart); -} - -bool QmitkDicomInspectorView::InitObservers() -{ - bool result = true; - - auto* timeNavigationController = mitk::RenderingManager::GetInstance()->GetTimeNavigationController(); - itk::SimpleMemberCommand::Pointer cmdTimeEvent = - itk::SimpleMemberCommand::New(); - cmdTimeEvent->SetCallbackFunction(this, &QmitkDicomInspectorView::OnTimeChanged); - m_ControllerToTimeObserverTag = timeNavigationController->AddObserver(mitk::TimeNavigationController::TimeEvent(0), cmdTimeEvent); - - typedef QHash WindowMapType; - WindowMapType windowMap = m_RenderWindowPart->GetQmitkRenderWindows(); + this->m_SliceNavigationListener.RenderWindowPartActivated(this->GetRenderWindowPart()); + connect(&m_SliceNavigationListener, &QmitkSliceNavigationListener::SliceChanged, this, &QmitkDicomInspectorView::OnSliceChanged); - auto i = windowMap.begin(); + auto selection = this->GetSite()->GetWorkbenchWindow()->GetSelectionService()->GetSelection(); + auto currentSelection = GetInitialSelection(selection); - while (i != windowMap.end()) - { - mitk::SliceNavigationController* sliceNavController = i.value()->GetSliceNavigationController(); - - if (nullptr != sliceNavController) - { - auto cmdSliceEvent = itk::SimpleMemberCommand::New(); - cmdSliceEvent->SetCallbackFunction(this, &QmitkDicomInspectorView::OnSliceChanged); - int tag = sliceNavController->AddObserver( - mitk::SliceNavigationController::GeometrySliceEvent(nullptr, 0), cmdSliceEvent); - - m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, - m_RenderWindowPart))); - - auto cmdDelEvent = itk::MemberCommand::New(); - cmdDelEvent->SetCallbackFunction(this, &QmitkDicomInspectorView::OnSliceNavigationControllerDeleted); - tag = sliceNavController->AddObserver(itk::DeleteEvent(), cmdDelEvent); - - m_ObserverMap.insert(std::make_pair(sliceNavController, ObserverInfo(sliceNavController, tag, - m_RenderWindowPart))); - } - - ++i; - - result = result && sliceNavController; - } - - return result; -} - -void QmitkDicomInspectorView::RemoveObservers(const mitk::SliceNavigationController* deletedSlicer) -{ - std::pair obsRange = - m_ObserverMap.equal_range(deletedSlicer); - - for (ObserverMapType::const_iterator pos = obsRange.first; pos != obsRange.second; ++pos) - { - pos->second.controller->RemoveObserver(pos->second.observerTag); - } - - m_ObserverMap.erase(deletedSlicer); -} - -void QmitkDicomInspectorView::RemoveAllObservers(mitk::IRenderWindowPart* deletedPart) -{ - for (ObserverMapType::const_iterator pos = m_ObserverMap.begin(); pos != m_ObserverMap.end();) - { - ObserverMapType::const_iterator delPos = pos++; - - if (nullptr == deletedPart || deletedPart == delPos->second.renderWindowPart) - { - delPos->second.controller->RemoveObserver(delPos->second.observerTag); - m_ObserverMap.erase(delPos); - } - } + if (!currentSelection.isEmpty()) + m_Controls.singleSlot->SetCurrentSelection(currentSelection); } void QmitkDicomInspectorView::OnCurrentSelectionChanged(QList nodes) @@ -197,30 +144,10 @@ void QmitkDicomInspectorView::OnCurrentSelectionChanged(QList(sender); - - this->RemoveObservers(sendingSlicer); -} - void QmitkDicomInspectorView::ValidateAndSetCurrentPosition() { const auto currentSelectedPosition = m_RenderWindowPart->GetSelectedPosition(); @@ -264,21 +191,7 @@ void QmitkDicomInspectorView::ValidateAndSetCurrentPosition() } } -void QmitkDicomInspectorView::OnSliceChangedDelayed() -{ - m_PendingSliceChangedEvent = false; - - ValidateAndSetCurrentPosition(); - - m_Controls.tableTags->setEnabled(m_ValidSelectedPosition); - - if (m_SelectedNode.IsNotNull()) - { - RenderTable(); - } -} - -void QmitkDicomInspectorView::OnTimeChanged() +void QmitkDicomInspectorView::OnSliceChanged() { ValidateAndSetCurrentPosition(); diff --git a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.h b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.h index b26f77fd278..74de75f831a 100644 --- a/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.h +++ b/Plugins/org.mitk.gui.qt.dicominspector/src/internal/QmitkDicomInspectorView.h @@ -28,6 +28,7 @@ found in the LICENSE file. // mitk gui qt common plugin #include #include +#include /** * @brief View class to inspect all DICOM tags available for the data of a node. @@ -53,34 +54,16 @@ class QmitkDicomInspectorView : public QmitkAbstractView, public mitk::IRenderWi void CreateQtPartControl(QWidget* parent) override; - /** @brief Initializes and sets the observers that are used to monitor changes in the selected position - or time point in order to actualize the view*/ - bool InitObservers(); - - /** @brief Removes all observers of the specific deleted slice navigation controller.*/ - void RemoveObservers(const mitk::SliceNavigationController* deletedSlicer); - - /** @brief Removes all observers of the deletedPart. If null pointer is passed all observers will be removed.*/ - void RemoveAllObservers(mitk::IRenderWindowPart* deletedPart = nullptr); - /** @brief Called by the selection widget when the selection has changed.*/ void OnCurrentSelectionChanged(QList nodes); - /** @brief Calls OnSliceChangedDelayed so the event isn't triggered multiple times.*/ - void OnSliceChanged(); - void OnTimeChanged(); - - void OnSliceNavigationControllerDeleted(const itk::Object* sender, const itk::EventObject& /*e*/); - /** @brief Sets m_currentSelectedPosition to the current selection and validates if this position is valid * for the input image of the currently selected fit. If it is valid, m_validSelectedPosition is set to true. * If the fit, his input image or geometry is not specified, it will also handled as invalid.*/ void ValidateAndSetCurrentPosition(); -private Q_SLOTS: - - /** @brief Updates the current slice and time is correctly displayed.*/ - void OnSliceChangedDelayed(); +protected slots: + void OnSliceChanged(); private: @@ -97,22 +80,7 @@ private Q_SLOTS: std::unique_ptr m_SelectionServiceConnector; - /** Needed for observing the events for when a slice or time step is changed.*/ - bool m_PendingSliceChangedEvent; - - /** Helper structure to manage the registered observer events.*/ - struct ObserverInfo - { - mitk::SliceNavigationController* controller; - int observerTag; - mitk::IRenderWindowPart* renderWindowPart; - - ObserverInfo(mitk::SliceNavigationController* controller, int observerTag, mitk::IRenderWindowPart* part); - }; - - typedef std::multimap ObserverMapType; - ObserverMapType m_ObserverMap; - unsigned int m_ControllerToTimeObserverTag; + QmitkSliceNavigationListener m_SliceNavigationListener; /** @brief Currently selected node for the DICOM information.*/ mitk::DataNode::ConstPointer m_SelectedNode; From d40973bca0c3cfa174322d794fa9d403d4d28c1d Mon Sep 17 00:00:00 2001 From: floca Date: Wed, 29 Nov 2023 16:31:15 +0100 Subject: [PATCH 92/92] - fixed regression in QmitkSliceNavigationListener that was introduced with https://phabricator.mitk.org/rMITK77eaee663ab1d9550cf85d0719e19a87500c072a but was not noticed so far (SliceChanged was not triggered anymore if it happened due to time change) --- .../org.mitk.gui.qt.common/src/QmitkSliceNavigationListener.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Plugins/org.mitk.gui.qt.common/src/QmitkSliceNavigationListener.cpp b/Plugins/org.mitk.gui.qt.common/src/QmitkSliceNavigationListener.cpp index 01b558111ed..93bc97b02ce 100644 --- a/Plugins/org.mitk.gui.qt.common/src/QmitkSliceNavigationListener.cpp +++ b/Plugins/org.mitk.gui.qt.common/src/QmitkSliceNavigationListener.cpp @@ -93,6 +93,7 @@ void QmitkSliceNavigationListener::OnTimeChangedInternal(itk::Object* sender, co if (newSelectedTimePoint != m_CurrentSelectedTimePoint) { m_CurrentSelectedTimePoint = newSelectedTimePoint; + emit SliceChanged(); emit SelectedTimePointChanged(newSelectedTimePoint); } }