diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox index ef6f9c5bfa..c66a8971dc 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Concepts.dox @@ -1,31 +1,32 @@ /** \page Concepts MITK Concepts The following items describe some issues about MITK on a more abstract level. -# \subpage CodingPage "Coding Concepts" -# \ref CodingPageGeneral -# \ref CodingPageStyle -# \ref CodingPageMITKMacros -# \subpage MicroServices_Overview -# Data Concepts -# \subpage BasicDataTypesPage -# \subpage DataManagementPage -# \subpage ReaderWriterPage -# \subpage MitkImagePage -# \subpage MITKSegmentationTaskListsPage + -# \subpage MITKROIPage -# \subpage PropertiesPage -# \subpage GeometryOverviewPage -# \subpage PipelineingConceptPage -# \subpage AnnotationPage -# \subpage PersistenceConceptPage -# \subpage SelectionConceptPage -# \subpage QVTKRendering -# Interaction -# \subpage DataInteractionPage -# \subpage InteractionPage -# \subpage LoggingPage -# \subpage ExceptionPage -# \subpage ModularizationPage "Modularization Concept" -# \ref ModularizationPageOverview */ diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md new file mode 100644 index 0000000000..2dac2c9742 --- /dev/null +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKROIs.md @@ -0,0 +1,154 @@ +# 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 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. + +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 + +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, + "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] + } + } + } + ] + } + ] +} +~~~ + +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/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md index ddbb5b926b..b070e52c16 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/MITKSegmentationTaskLists.md @@ -1,145 +1,144 @@ # MITK Segmentation Task Lists {#MITKSegmentationTaskListsPage} [TOC] ## Overview MITK Segmentation Task Lists are a JSON-based file format defining a list of segmentation tasks. Segmentation tasks consist at least of a path to a reference image and a unique result path. The result path specifies where the final segmentation of the task is expected to be located once it is done. Optional properties of a segmentation task include a task name and description as well as various degrees of start conditions for the segmentation like a label name, a list of suggested names and colors for new labels, a label set preset, or even a pre-segmentation to begin with. 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 MITK Segmentation Task Lists are JSON files containing a JSON object as root, which must contain the two mandatory properties `FileFormat` and `Version`: ~~~{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1 } ~~~ We also recommend to specify an optional `Name` that is used in the application if present instead of the plain filename of the JSON file: ~~~{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1, "Name": "My First Task List" } ~~~ ### Tasks The root object must also contain a mandatory `Tasks` array containing JSON objects that specify the individual tasks of the task list. A minimum task object must consist of `Image` and `Result` file paths. `Image` is a path to the reference image for the task and `Result` is a path where the final segmentation is expected to be stored once it is done. Paths can be absolute or relative to the MITK Segmentation Task List file. ~~~{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1, "Tasks": [ { "Image": "images/input.nrrd", "Result": "results/output.nrrd" } ] } ~~~ In addition, tasks can define various optional properties that mainly specify the start conditions for a segmentation: - `Name` (*string*): A name for the task. - `Description` (*string*): A short description/definition of the task. - `LabelName` (*string*): The name of the first label in a new segmentation that is created for the task on the fly. - `LabelNameSuggestions` (*file path*): A Label Suggestions JSON file specifying names and optional colors that are suggested to the user for new labels in the segmentation. - `Preset` (*file path*): A Label Set Preset XML file in MITK's .lsetp file format. The preset is applied to a new segmentation that is created for the task on the fly. We recommend to use the Segmentation plugin to create such label set preset files as described in its [user guide](@ref org_mitk_views_segmentationlabelpresets). - `Segmentation` (*file path*): A pre-segmentation that a user can start with or should refine. - `Dynamic` (*boolean*): In case `Image` refers to a dynamic (3d+t) image, specifies whether the segmentation should be static (*false*), i.e. equal for all time steps, or dynamic (*true*), i.e. individual for each time step. ### Task defaults / common properties If a task list contains multiple tasks with common properties, they do not have to be specified for each and every task again and again. Instead, the root object may contain an optional `Defaults` object that is identical in format to the tasks specified above. There is one exception, though: A `Defaults` object must not contain a `Result` file path, since result files of tasks must be distinct by definition. As the name indicates, default properties can still be overridden by individual tasks if they are specified explicitly. ### Example The following example is a complete showcase of the properties and features listed above. It defines four tasks, three of which refer to the same reference image so it is specified as default. Remember that the only task property required to be distinct is `Result` so you are pretty free in your task design. For simplicity, we chose to define tasks around organs for this example and named the tasks accordingly: ~~~{.json} { "FileFormat": "MITK Segmentation Task List", "Version": 1, "Name": "Example Segmentation Task List", "Defaults": { "Image": "images/Pic3D.nrrd" }, "Tasks": [ { "Name": "Liver", "LabelName": "Liver", "LabelNameSuggestions": "suggestions/label_suggestions.json", "Description": "This task provides an image and label name suggestions for new labels. The segmentation will start with an empty label named Liver.", "Result": "results/liver.nrrd" }, { "Name": "Kidneys", "Description": "This task provides an image and a label set preset that is applied to the new segmentation.", "Preset": "presets/kidneys.lsetp", "Result": "results/kidneys.nrrd" }, { "Name": "Spleen", "Description": "This task provides an image and an initial (pre-)segmentation.", "Segmentation": "segmentations/spleen.nrrd", "Result": "results/spleen.nrrd" }, { "Name": "Surprise", "Description": "And now for something completely different. This task overrides the default Image and starts with an empty static segmentation for a dynamic image.", "Image": "images/US4DCyl.nrrd", "Result": "results/US4DCyl.nrrd", "Dynamic": false } ] } ~~~ ## Label name suggestions The `LabelNameSuggestions` property of segmentation tasks is supposed to reference a JSON file that consists of an array of objects with a mandatory `name` property and an optional `color` property. For example: ~~~{.json} [ { "name": "Abdomen", "color": "red" }, { "name": "Lung", "color": "#00ff00" }, { "name": "Heart" }, { "name": "Aortic Valve", "color": "CornflowerBlue" } ] ~~~ diff --git a/Modules/AppUtil/src/mitkBaseApplication.cpp b/Modules/AppUtil/src/mitkBaseApplication.cpp index 68948a6607..91201a6414 100644 --- a/Modules/AppUtil/src/mitkBaseApplication.cpp +++ b/Modules/AppUtil/src/mitkBaseApplication.cpp @@ -1,902 +1,905 @@ /*============================================================================ 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 <mitkBaseApplication.h> #include <mitkCoreServices.h> #include <mitkIPreferencesService.h> #include <mitkExceptionMacro.h> #include <mitkLog.h> #include <mitkProvisioningInfo.h> #include <QmitkSafeApplication.h> #include <QmitkSingleApplication.h> #include <Poco/Util/HelpFormatter.h> #include <ctkPluginFramework.h> #include <ctkPluginFramework_global.h> #include <ctkPluginFrameworkLauncher.h> #include <usModuleSettings.h> +#include <vtkLogger.h> #include <vtkOpenGLRenderWindow.h> #include <QVTKOpenGLNativeWidget.h> #include <QCoreApplication> #include <QDir> #include <QFileInfo> #include <QRunnable> #include <QSplashScreen> #include <QStandardPaths> #include <QTime> #include <QWebEngineUrlScheme> namespace { void outputQtMessage(QtMsgType type, const QMessageLogContext&, const QString& msg) { auto message = msg.toStdString(); switch (type) { case QtDebugMsg: MITK_DEBUG << message; break; case QtInfoMsg: MITK_INFO << message; break; case QtWarningMsg: MITK_WARN << message; break; case QtCriticalMsg: MITK_ERROR << message; break; case QtFatalMsg: MITK_ERROR << message; abort(); default: MITK_INFO << message; break; } } } namespace mitk { const QString BaseApplication::ARG_APPLICATION = "BlueBerry.application"; const QString BaseApplication::ARG_CLEAN = "BlueBerry.clean"; const QString BaseApplication::ARG_CONSOLELOG = "BlueBerry.consoleLog"; const QString BaseApplication::ARG_DEBUG = "BlueBerry.debug"; const QString BaseApplication::ARG_FORCE_PLUGIN_INSTALL = "BlueBerry.forcePlugins"; const QString BaseApplication::ARG_HOME = "BlueBerry.home"; const QString BaseApplication::ARG_NEWINSTANCE = "BlueBerry.newInstance"; const QString BaseApplication::ARG_NO_LAZY_REGISTRY_CACHE_LOADING = "BlueBerry.noLazyRegistryCacheLoading"; const QString BaseApplication::ARG_NO_REGISTRY_CACHE = "BlueBerry.noRegistryCache"; const QString BaseApplication::ARG_PLUGIN_CACHE = "BlueBerry.plugin_cache_dir"; const QString BaseApplication::ARG_PLUGIN_DIRS = "BlueBerry.plugin_dirs"; const QString BaseApplication::ARG_PRELOAD_LIBRARY = "BlueBerry.preloadLibrary"; const QString BaseApplication::ARG_PRODUCT = "BlueBerry.product"; const QString BaseApplication::ARG_PROVISIONING = "BlueBerry.provisioning"; const QString BaseApplication::ARG_REGISTRY_MULTI_LANGUAGE = "BlueBerry.registryMultiLanguage"; const QString BaseApplication::ARG_SPLASH_IMAGE = "BlueBerry.splashscreen"; const QString BaseApplication::ARG_STORAGE_DIR = "BlueBerry.storageDir"; const QString BaseApplication::ARG_XARGS = "xargs"; const QString BaseApplication::ARG_LOG_QT_MESSAGES = "Qt.logMessages"; const QString BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET = "Segmentation.labelSetPreset"; const QString BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS = "Segmentation.labelSuggestions"; const QString BaseApplication::PROP_APPLICATION = "blueberry.application"; const QString BaseApplication::PROP_FORCE_PLUGIN_INSTALL = BaseApplication::ARG_FORCE_PLUGIN_INSTALL; const QString BaseApplication::PROP_NEWINSTANCE = BaseApplication::ARG_NEWINSTANCE; const QString BaseApplication::PROP_NO_LAZY_REGISTRY_CACHE_LOADING = BaseApplication::ARG_NO_LAZY_REGISTRY_CACHE_LOADING; const QString BaseApplication::PROP_NO_REGISTRY_CACHE = BaseApplication::ARG_NO_REGISTRY_CACHE; const QString BaseApplication::PROP_PRODUCT = "blueberry.product"; const QString BaseApplication::PROP_REGISTRY_MULTI_LANGUAGE = BaseApplication::ARG_REGISTRY_MULTI_LANGUAGE; class SplashCloserCallback : public QRunnable { public: SplashCloserCallback(QSplashScreen* splashscreen) : m_Splashscreen(splashscreen) { } void run() override { this->m_Splashscreen->close(); } private: QSplashScreen *m_Splashscreen; // Owned by BaseApplication::Impl }; struct BaseApplication::Impl { ctkProperties m_FWProps; QCoreApplication *m_QApp; int m_Argc; char **m_Argv; #ifdef Q_OS_MAC std::vector<char*> m_Argv_macOS; #endif QString m_AppName; QString m_OrgaName; QString m_OrgaDomain; bool m_SingleMode; bool m_SafeMode; QSplashScreen *m_Splashscreen; SplashCloserCallback *m_SplashscreenClosingCallback; bool m_LogQtMessages; QStringList m_PreloadLibs; QString m_ProvFile; Impl(int argc, char **argv) : m_Argc(argc), m_Argv(argv), #ifdef Q_OS_MAC m_Argv_macOS(), #endif m_SingleMode(false), m_SafeMode(true), m_Splashscreen(nullptr), m_SplashscreenClosingCallback(nullptr), m_LogQtMessages(false) { #ifdef Q_OS_MAC /* On macOS the process serial number is passed as an command line argument (-psn_<NUMBER>) in certain circumstances. This option causes a Poco exception. We remove it, if present. */ m_Argv_macOS.reserve(argc + 1); const char psn[] = "-psn"; for (int i = 0; i < argc; ++i) { if (0 == strncmp(argv[i], psn, sizeof(psn) - 1)) continue; m_Argv_macOS.push_back(argv[i]); } m_Argv_macOS.push_back(nullptr); m_Argc = static_cast<decltype(m_Argc)>(m_Argv_macOS.size() - 1); m_Argv = m_Argv_macOS.data(); #endif } ~Impl() { delete m_SplashscreenClosingCallback; delete m_Splashscreen; delete m_QApp; } QVariant getProperty(const QString &property) const { auto iter = m_FWProps.find(property); return m_FWProps.end() != iter ? iter.value() : QVariant(); } void handleBooleanOption(const std::string &name, const std::string &) { if (ARG_LOG_QT_MESSAGES.toStdString() == name) { m_LogQtMessages = true; return; } auto fwKey = QString::fromStdString(name); // Translate some keys to proper framework properties if (ARG_CONSOLELOG == fwKey) fwKey = ctkPluginFrameworkLauncher::PROP_CONSOLE_LOG; // For all other options we use the command line option name as the // framework property key. m_FWProps[fwKey] = true; } void handlePreloadLibraryOption(const std::string &, const std::string &value) { m_PreloadLibs.push_back(QString::fromStdString(value)); } void handleClean(const std::string &, const std::string &) { m_FWProps[ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN] = ctkPluginConstants::FRAMEWORK_STORAGE_CLEAN_ONFIRSTINIT; } void initializeCTKPluginFrameworkProperties(Poco::Util::LayeredConfiguration &configuration) { // Add all configuration key/value pairs as framework properties Poco::Util::LayeredConfiguration::Keys keys; Poco::Util::LayeredConfiguration::Keys keyStack; configuration.keys(keyStack); std::vector<std::string> keyChain; while (!keyStack.empty()) { const auto currSubKey = keyStack.back(); if (!keyChain.empty() && keyChain.back() == currSubKey) { keyChain.pop_back(); keyStack.pop_back(); continue; } Poco::Util::LayeredConfiguration::Keys subKeys; configuration.keys(currSubKey, subKeys); if (subKeys.empty()) { std::string finalKey; keyStack.pop_back(); for (const auto& key : keyChain) finalKey += key + '.'; finalKey += currSubKey; keys.push_back(finalKey); } else { keyChain.push_back(currSubKey); for (const auto& key : subKeys) keyStack.push_back(key); } } for (const auto& key : keys) { if (configuration.hasProperty(key)) { // .ini and command line options overwrite already inserted keys auto qKey = QString::fromStdString(key); m_FWProps[qKey] = QString::fromStdString(configuration.getString(key)); } } } void parseProvisioningFile(const QString &filePath) { // Skip parsing if the file path is empty if (filePath.isEmpty()) return; auto consoleLog = this->getProperty(ctkPluginFrameworkLauncher::PROP_CONSOLE_LOG).toBool(); // Read initial plugins from a provisioning file QFileInfo provFile(filePath); QStringList pluginsToStart; if (provFile.exists()) { MITK_INFO(consoleLog) << "Using provisioning file: " << qPrintable(provFile.absoluteFilePath()); ProvisioningInfo provInfo(provFile.absoluteFilePath()); // It can still happen that the encoding is not compatible with the fromUtf8 function (i.e. when // manipulating the LANG variable). The QStringList in provInfo is empty then. if (provInfo.getPluginDirs().empty()) { MITK_ERROR << "Cannot search for provisioning file, the retrieved directory list is empty.\n" << "This can happen if there are some special non-ASCII characters in the install path."; } else { for(const auto& pluginPath : provInfo.getPluginDirs()) ctkPluginFrameworkLauncher::addSearchPath(pluginPath); auto pluginUrlsToStart = provInfo.getPluginsToStart(); for (const auto& url : qAsConst(pluginUrlsToStart)) pluginsToStart.push_back(url.toString()); } } else { MITK_INFO(consoleLog) << "Provisionig file does not exist."; } if (!pluginsToStart.isEmpty()) { m_FWProps[ctkPluginFrameworkLauncher::PROP_PLUGINS] = pluginsToStart; // Use transient start with declared activation policy (this helps when the provisioning file // changes and some plug-ins should not be installed in the application any more). ctkPlugin::StartOptions startOptions(ctkPlugin::START_TRANSIENT | ctkPlugin::START_ACTIVATION_POLICY); m_FWProps[ctkPluginFrameworkLauncher::PROP_PLUGINS_START_OPTIONS] = static_cast<int>(startOptions); } } }; BaseApplication::BaseApplication(int argc, char **argv) : Application(), d(new Impl(argc, argv)) { } BaseApplication::~BaseApplication() { delete d; } void BaseApplication::printHelp(const std::string &, const std::string &) { Poco::Util::HelpFormatter help(this->options()); help.setAutoIndent(); help.setCommand(this->commandName()); help.format(std::cout); exit(EXIT_OK); } void BaseApplication::setApplicationName(const QString &name) { if (nullptr != qApp) qApp->setApplicationName(name); d->m_AppName = name; } QString BaseApplication::getApplicationName() const { return nullptr != qApp ? qApp->applicationName() : d->m_AppName; } void BaseApplication::setOrganizationName(const QString &name) { if (nullptr != qApp) qApp->setOrganizationName(name); d->m_OrgaName = name; } QString BaseApplication::getOrganizationName() const { return nullptr != qApp ? qApp->organizationName() : d->m_OrgaName; } void BaseApplication::setOrganizationDomain(const QString &domain) { if (nullptr != qApp) qApp->setOrganizationDomain(domain); d->m_OrgaDomain = domain; } QString BaseApplication::getOrganizationDomain() const { return nullptr != qApp ? qApp->organizationDomain() : d->m_OrgaDomain; } void BaseApplication::setSingleMode(bool singleMode) { if (nullptr != qApp) return; d->m_SingleMode = singleMode; } bool BaseApplication::getSingleMode() const { return d->m_SingleMode; } void BaseApplication::setSafeMode(bool safeMode) { if (nullptr != qApp && nullptr == d->m_QApp) return; d->m_SafeMode = safeMode; nullptr == d->m_QApp && getSingleMode() ? static_cast<QmitkSingleApplication *>(d->m_QApp)->setSafeMode(safeMode) : static_cast<QmitkSafeApplication *>(d->m_QApp)->setSafeMode(safeMode); } bool BaseApplication::getSafeMode() const { return d->m_SafeMode; } void BaseApplication::setPreloadLibraries(const QStringList &libraryBaseNames) { d->m_PreloadLibs = libraryBaseNames; } QStringList BaseApplication::getPreloadLibraries() const { return d->m_PreloadLibs; } void BaseApplication::setProvisioningFilePath(const QString &filePath) { d->m_ProvFile = filePath; } QString BaseApplication::getProvisioningFilePath() const { auto provFilePath = d->m_ProvFile; // A null QString means look up a default provisioning file if (provFilePath.isNull() && nullptr != qApp) { QFileInfo appFilePath(QCoreApplication::applicationFilePath()); QDir basePath(QCoreApplication::applicationDirPath()); auto provFileName = appFilePath.baseName() + ".provisioning"; QFileInfo provFile(basePath.absoluteFilePath(provFileName)); #ifdef Q_OS_MAC /* * On macOS, if started from the build directory, the .provisioning file is located at: * <MITK-build/bin/MitkWorkbench.provisioning> * The executable path is: * <MITK-build/bin/MitkWorkbench.app/Contents/MacOS/MitkWorkbench> * In this case we have to cdUp threetimes. * * During packaging the MitkWorkbench.provisioning file is placed at the same * level like the executable. Nothing has to be done. */ if (!provFile.exists()) { basePath.cdUp(); basePath.cdUp(); basePath.cdUp(); provFile = basePath.absoluteFilePath(provFileName); } #endif if (provFile.exists()) { provFilePath = provFile.absoluteFilePath(); } #ifdef CMAKE_INTDIR else { basePath.cdUp(); provFile.setFile(basePath.absoluteFilePath(provFileName)); if (provFile.exists()) provFilePath = provFile.absoluteFilePath(); } #endif } return provFilePath; } void BaseApplication::initializeQt() { if (nullptr != qApp) return; #ifdef Q_OS_LINUX qputenv("QTWEBENGINE_CHROMIUM_FLAGS", "--single-process"); // See T29332 #endif // If parameters have been set before, we have to store them to hand them // through to the application auto appName = this->getApplicationName(); auto orgName = this->getOrganizationName(); auto orgDomain = this->getOrganizationDomain(); // Create a QCoreApplication instance this->getQApplication(); // Provide parameters to QCoreApplication this->setApplicationName(appName); this->setOrganizationName(orgName); this->setOrganizationDomain(orgDomain); if (d->m_LogQtMessages) qInstallMessageHandler(outputQtMessage); QWebEngineUrlScheme qtHelpScheme("qthelp"); qtHelpScheme.setFlags(QWebEngineUrlScheme::LocalScheme | QWebEngineUrlScheme::LocalAccessAllowed); QWebEngineUrlScheme::registerScheme(qtHelpScheme); } void BaseApplication::initialize(Poco::Util::Application &self) { // 1. Call the super-class method Poco::Util::Application::initialize(self); // 2. Initialize the Qt framework (by creating a QCoreApplication) this->initializeQt(); // 3. Seed the random number generator, once at startup. QTime time = QTime::currentTime(); qsrand((uint)time.msec()); // 4. Load the "default" configuration, which involves parsing // an optional <executable-name>.ini file and parsing any // command line arguments this->loadConfiguration(); // 5. Add configuration data from the command line and the // optional <executable-name>.ini file as CTK plugin // framework properties. d->initializeCTKPluginFrameworkProperties(this->config()); // 6. Initialize splash screen if an image path is provided // in the .ini file this->initializeSplashScreen(qApp); // 7. Set the custom CTK Plugin Framework storage directory QString storageDir = this->getCTKFrameworkStorageDir(); if (!storageDir.isEmpty()) { d->m_FWProps[ctkPluginConstants::FRAMEWORK_STORAGE] = storageDir; // Initialize core service preferences at the exact same location as their predecessor BlueBerry preferences mitk::CoreServicePointer preferencesService(mitk::CoreServices::GetPreferencesService()); preferencesService->InitializeStorage(storageDir.toStdString() + "/data/3/prefs.xml"); } // 8. Set the library search paths and the pre-load library property this->initializeLibraryPaths(); auto preloadLibs = this->getPreloadLibraries(); if (!preloadLibs.isEmpty()) d->m_FWProps[ctkPluginConstants::FRAMEWORK_PRELOAD_LIBRARIES] = preloadLibs; // 9. Initialize the CppMicroServices library. // The initializeCppMicroServices() method reuses the // FRAMEWORK_STORAGE property, so we call it after the // getCTKFrameworkStorageDir method. this->initializeCppMicroServices(); // 10. Parse the (optional) provisioning file and set the // correct framework properties. d->parseProvisioningFile(this->getProvisioningFilePath()); // 11. Set the CTK Plugin Framework properties ctkPluginFrameworkLauncher::setFrameworkProperties(d->m_FWProps); } void BaseApplication::uninitialize() { auto pfw = this->getFramework(); if (pfw) { pfw->stop(); // Wait for up to 10 seconds for the CTK plugin framework to stop pfw->waitForStop(10000); } Poco::Util::Application::uninitialize(); } int BaseApplication::getArgc() const { return d->m_Argc; } char **BaseApplication::getArgv() const { return d->m_Argv; } QString BaseApplication::getCTKFrameworkStorageDir() const { QString storageDir; if (this->getSingleMode()) { // This function checks if an instance is already running and either sends a message to // it containing the command line arguments or checks if a new instance was forced by // providing the BlueBerry.newInstance command line argument. In the latter case, a path // to a temporary directory for the new application's storage directory is returned. storageDir = handleNewAppInstance(static_cast<QtSingleApplication *>(d->m_QApp), d->m_Argc, d->m_Argv, ARG_NEWINSTANCE); } if (storageDir.isEmpty()) { // This is a new instance and no other instance is already running. We specify the // storage directory here (this is the same code as in berryInternalPlatform.cpp) // so that we can re-use the location for the persistent data location of the // the CppMicroServices library. // Append a hash value of the absolute path of the executable to the data location. // This allows to start the same application from different build or install trees. storageDir = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + "/" + this->getOrganizationName() + "/" + this->getApplicationName() + '_'; storageDir += QString::number(qHash(QCoreApplication::applicationDirPath())) + "/"; } return storageDir; } void BaseApplication::initializeCppMicroServices() { auto storageDir = this->getProperty(ctkPluginConstants::FRAMEWORK_STORAGE).toString(); if (!storageDir.isEmpty()) us::ModuleSettings::SetStoragePath((storageDir + "us" + QDir::separator()).toStdString()); } QCoreApplication *BaseApplication::getQApplication() const { if (nullptr == qApp) { + vtkLogger::SetStderrVerbosity(vtkLogger::VERBOSITY_WARNING); + vtkOpenGLRenderWindow::SetGlobalMaximumNumberOfMultiSamples(0); auto defaultFormat = QVTKOpenGLNativeWidget::defaultFormat(); defaultFormat.setSamples(0); QSurfaceFormat::setDefaultFormat(defaultFormat); #ifdef Q_OS_OSX QCoreApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); #endif QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); #endif QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); d->m_QApp = this->getSingleMode() ? static_cast<QCoreApplication*>(new QmitkSingleApplication(d->m_Argc, d->m_Argv, this->getSafeMode())) : static_cast<QCoreApplication*>(new QmitkSafeApplication(d->m_Argc, d->m_Argv, this->getSafeMode())); } return qApp; } void BaseApplication::initializeLibraryPaths() { QStringList suffixes; suffixes << "plugins"; #ifdef Q_OS_WINDOWS suffixes << "bin/plugins"; #ifdef CMAKE_INTDIR suffixes << "bin/" CMAKE_INTDIR "/plugins"; #endif #else suffixes << "lib/plugins"; #ifdef CMAKE_INTDIR suffixes << "lib/" CMAKE_INTDIR "/plugins"; #endif #endif #ifdef Q_OS_MAC suffixes << "../../plugins"; #endif // We add a couple of standard library search paths for plug-ins QDir appDir(QCoreApplication::applicationDirPath()); // Walk one directory up and add bin and lib sub-dirs; this might be redundant appDir.cdUp(); for (const auto& suffix : qAsConst(suffixes)) ctkPluginFrameworkLauncher::addSearchPath(appDir.absoluteFilePath(suffix)); } int BaseApplication::main(const std::vector<std::string> &args) { // Start the plugin framework and all installed plug-ins according to their auto-start setting QStringList arguments; for (auto const &arg : args) arguments.push_back(QString::fromStdString(arg)); if (nullptr != d->m_Splashscreen) { // A splash screen is displayed. Create the closing callback. d->m_SplashscreenClosingCallback = new SplashCloserCallback(d->m_Splashscreen); } return ctkPluginFrameworkLauncher::run(d->m_SplashscreenClosingCallback, QVariant::fromValue(arguments)).toInt(); } void BaseApplication::defineOptions(Poco::Util::OptionSet &options) { Poco::Util::Option helpOption("help", "h", "print this help text"); helpOption.callback(Poco::Util::OptionCallback<BaseApplication>(this, &BaseApplication::printHelp)); options.addOption(helpOption); Poco::Util::Option newInstanceOption(ARG_NEWINSTANCE.toStdString(), "", "forces a new instance of this application"); newInstanceOption.callback(Poco::Util::OptionCallback<Impl>(d, &Impl::handleBooleanOption)); options.addOption(newInstanceOption); Poco::Util::Option cleanOption(ARG_CLEAN.toStdString(), "", "cleans the plugin cache"); cleanOption.callback(Poco::Util::OptionCallback<Impl>(d, &Impl::handleClean)); options.addOption(cleanOption); Poco::Util::Option productOption(ARG_PRODUCT.toStdString(), "", "the id of the product to be launched"); productOption.argument("<id>").binding(PROP_PRODUCT.toStdString()); options.addOption(productOption); Poco::Util::Option appOption(ARG_APPLICATION.toStdString(), "", "the id of the application extension to be executed"); appOption.argument("<id>").binding(PROP_APPLICATION.toStdString()); options.addOption(appOption); Poco::Util::Option provOption(ARG_PROVISIONING.toStdString(), "", "the location of a provisioning file"); provOption.argument("<prov file>").binding(ARG_PROVISIONING.toStdString()); options.addOption(provOption); Poco::Util::Option storageDirOption(ARG_STORAGE_DIR.toStdString(), "", "the location for storing persistent application data"); storageDirOption.argument("<dir>").binding(ctkPluginConstants::FRAMEWORK_STORAGE.toStdString()); options.addOption(storageDirOption); Poco::Util::Option consoleLogOption(ARG_CONSOLELOG.toStdString(), "", "log messages to the console"); consoleLogOption.callback(Poco::Util::OptionCallback<Impl>(d, &Impl::handleBooleanOption)); options.addOption(consoleLogOption); Poco::Util::Option debugOption(ARG_DEBUG.toStdString(), "", "enable debug mode"); debugOption.argument("<options file>", false).binding(ctkPluginFrameworkLauncher::PROP_DEBUG.toStdString()); options.addOption(debugOption); Poco::Util::Option forcePluginOption(ARG_FORCE_PLUGIN_INSTALL.toStdString(), "", "force installing plug-ins with same symbolic name"); forcePluginOption.callback(Poco::Util::OptionCallback<Impl>(d, &Impl::handleBooleanOption)); options.addOption(forcePluginOption); Poco::Util::Option preloadLibsOption(ARG_PRELOAD_LIBRARY.toStdString(), "", "preload a library"); preloadLibsOption.argument("<library>") .repeatable(true) .callback(Poco::Util::OptionCallback<Impl>(d, &Impl::handlePreloadLibraryOption)); options.addOption(preloadLibsOption); Poco::Util::Option noRegistryCacheOption(ARG_NO_REGISTRY_CACHE.toStdString(), "", "do not use a cache for the registry"); noRegistryCacheOption.callback(Poco::Util::OptionCallback<Impl>(d, &Impl::handleBooleanOption)); options.addOption(noRegistryCacheOption); Poco::Util::Option noLazyRegistryCacheLoadingOption(ARG_NO_LAZY_REGISTRY_CACHE_LOADING.toStdString(), "", "do not use lazy cache loading for the registry"); noLazyRegistryCacheLoadingOption.callback(Poco::Util::OptionCallback<Impl>(d, &Impl::handleBooleanOption)); options.addOption(noLazyRegistryCacheLoadingOption); Poco::Util::Option registryMultiLanguageOption(ARG_REGISTRY_MULTI_LANGUAGE.toStdString(), "", "enable multi-language support for the registry"); registryMultiLanguageOption.callback(Poco::Util::OptionCallback<Impl>(d, &Impl::handleBooleanOption)); options.addOption(registryMultiLanguageOption); Poco::Util::Option splashScreenOption(ARG_SPLASH_IMAGE.toStdString(), "", "optional picture to use as a splash screen"); splashScreenOption.argument("<filename>").binding(ARG_SPLASH_IMAGE.toStdString()); options.addOption(splashScreenOption); Poco::Util::Option xargsOption(ARG_XARGS.toStdString(), "", "Extended argument list"); xargsOption.argument("<args>").binding(ARG_XARGS.toStdString()); options.addOption(xargsOption); Poco::Util::Option logQtMessagesOption(ARG_LOG_QT_MESSAGES.toStdString(), "", "log Qt messages"); logQtMessagesOption.callback(Poco::Util::OptionCallback<Impl>(d, &Impl::handleBooleanOption)); options.addOption(logQtMessagesOption); Poco::Util::Option labelSetPresetOption(ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), "", "use this label set preset for new segmentations"); labelSetPresetOption.argument("<filename>").binding(ARG_SEGMENTATION_LABELSET_PRESET.toStdString()); options.addOption(labelSetPresetOption); Poco::Util::Option labelSuggestionsOption(ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), "", "use this list of predefined suggestions for segmentation labels"); labelSuggestionsOption.argument("<filename>").binding(ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString()); options.addOption(labelSuggestionsOption); Poco::Util::Application::defineOptions(options); } QSharedPointer<ctkPluginFramework> BaseApplication::getFramework() const { return ctkPluginFrameworkLauncher::getPluginFramework(); } ctkPluginContext *BaseApplication::getFrameworkContext() const { auto framework = getFramework(); return framework ? framework->getPluginContext() : nullptr; } void BaseApplication::initializeSplashScreen(QCoreApplication * application) const { auto pixmapFileNameProp = d->getProperty(ARG_SPLASH_IMAGE); if (!pixmapFileNameProp.isNull()) { auto pixmapFileName = pixmapFileNameProp.toString(); QFileInfo checkFile(pixmapFileName); if (checkFile.exists() && checkFile.isFile()) { QPixmap pixmap(checkFile.absoluteFilePath()); d->m_Splashscreen = new QSplashScreen(pixmap, Qt::WindowStaysOnTopHint); d->m_Splashscreen->show(); application->processEvents(); } } } QHash<QString, QVariant> BaseApplication::getFrameworkProperties() const { return d->m_FWProps; } int BaseApplication::run() { this->init(d->m_Argc, d->m_Argv); return Application::run(); } void BaseApplication::setProperty(const QString &property, const QVariant &value) { d->m_FWProps[property] = value; } QVariant BaseApplication::getProperty(const QString &property) const { return d->getProperty(property); } void BaseApplication::installTranslator(QTranslator* translator) { this->getQApplication()->installTranslator(translator); } bool BaseApplication::isRunning() { auto app = dynamic_cast<QtSingleApplication*>(this->getQApplication()); if (nullptr != app) app->isRunning(); mitkThrow() << "Method not implemented."; } void BaseApplication::sendMessage(const QByteArray msg) { auto app = dynamic_cast<QtSingleApplication*>(this->getQApplication()); if (nullptr != app) app->sendMessage(msg); mitkThrow() << "Method not implemented."; } } diff --git a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h index f3eebac5e3..1c67809032 100644 --- a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h +++ b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper2D.h @@ -1,83 +1,83 @@ /*============================================================================ 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 mitkBoundingShapeVtkMapper2D_h #define mitkBoundingShapeVtkMapper2D_h #include <mitkVtkMapper.h> #include <MitkBoundingShapeExports.h> #include <vtkActor2D.h> #include <vtkCutter.h> #include <vtkPlane.h> #include <vtkPolyDataMapper2D.h> #include <vtkPropAssembly.h> #include <vtkSmartPointer.h> -#include <vtkSphereSource.h> +#include <vtkCubeSource.h> namespace mitk { class MITKBOUNDINGSHAPE_EXPORT BoundingShapeVtkMapper2D final : public VtkMapper { class LocalStorage : public Mapper::BaseLocalStorage { public: LocalStorage(); ~LocalStorage() override; bool IsUpdateRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode); vtkSmartPointer<vtkActor> m_Actor; vtkSmartPointer<vtkActor2D> m_HandleActor; vtkSmartPointer<vtkActor2D> m_SelectedHandleActor; vtkSmartPointer<vtkPolyDataMapper> m_Mapper; vtkSmartPointer<vtkPolyDataMapper2D> m_HandleMapper; vtkSmartPointer<vtkPolyDataMapper2D> m_SelectedHandleMapper; vtkSmartPointer<vtkCutter> m_Cutter; vtkSmartPointer<vtkPlane> m_CuttingPlane; unsigned int m_LastSliceNumber; - std::vector<vtkSmartPointer<vtkSphereSource>> m_Handles; + std::vector<vtkSmartPointer<vtkCubeSource>> m_Handles; vtkSmartPointer<vtkPropAssembly> m_PropAssembly; double m_ZoomFactor; private: LocalStorage(const LocalStorage &); LocalStorage &operator=(const LocalStorage &); }; public: static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer = nullptr, bool overwrite = false); mitkClassMacro(BoundingShapeVtkMapper2D, VtkMapper); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void ApplyColorAndOpacityProperties(BaseRenderer *, vtkActor *) override; vtkProp *GetVtkProp(BaseRenderer *renderer) override; private: BoundingShapeVtkMapper2D(); ~BoundingShapeVtkMapper2D() override; BoundingShapeVtkMapper2D(const Self &); Self &operator=(const Self &); void GenerateDataForRenderer(BaseRenderer *renderer) override; void Update(mitk::BaseRenderer *renderer) override; class Impl; Impl *m_Impl; }; } #endif diff --git a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h index 4e800576b0..3af3bdfe70 100644 --- a/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h +++ b/Modules/BoundingShape/include/mitkBoundingShapeVtkMapper3D.h @@ -1,56 +1,51 @@ /*============================================================================ 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 mitkBoundingShapeVtkMapper3D_h #define mitkBoundingShapeVtkMapper3D_h #include <MitkBoundingShapeExports.h> - #include <mitkVtkMapper.h> -#include <vtkPropAssembly.h> -#include <vtkSmartPointer.h> -#include <vtkSphereSource.h> - namespace mitk { class MITKBOUNDINGSHAPE_EXPORT BoundingShapeVtkMapper3D : public VtkMapper { public: static void SetDefaultProperties(DataNode *node, BaseRenderer *renderer = nullptr, bool overwrite = false); mitkClassMacro(BoundingShapeVtkMapper3D, VtkMapper); itkFactorylessNewMacro(Self); itkCloneMacro(Self); void ApplyColorAndOpacityProperties(BaseRenderer *, vtkActor *) override; void ApplyBoundingShapeProperties(BaseRenderer *renderer, vtkActor *); vtkProp *GetVtkProp(BaseRenderer *renderer) override; // virtual void UpdateVtkTransform(mitk::BaseRenderer* renderer) override; protected: void GenerateDataForRenderer(BaseRenderer *renderer) override; private: BoundingShapeVtkMapper3D(); ~BoundingShapeVtkMapper3D() override; BoundingShapeVtkMapper3D(const Self &); Self &operator=(const Self &); class Impl; Impl *m_Impl; }; } #endif diff --git a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp index 9c61cf79ae..27a7210bb2 100644 --- a/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp +++ b/Modules/BoundingShape/src/Interactions/mitkBoundingShapeInteractor.cpp @@ -1,605 +1,605 @@ /*============================================================================ 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 "../DataManagement/mitkBoundingShapeUtil.h" #include <mitkBoundingShapeInteractor.h> #include <mitkDisplayActionEventBroadcast.h> #include <mitkInteractionConst.h> #include <mitkInteractionEventObserver.h> #include <mitkInteractionKeyEvent.h> #include <mitkInteractionPositionEvent.h> #include <mitkMouseWheelEvent.h> #include <vtkCamera.h> #include <vtkInteractorObserver.h> #include <vtkInteractorStyle.h> #include <vtkPointData.h> #include <vtkRenderWindowInteractor.h> #include <vtkSmartPointer.h> #include "usGetModuleContext.h" #include "usModuleRegistry.h" // Properties to allow the user to interact with the base data const char *selectedColorPropertyName = "Bounding Shape.Selected Color"; const char *deselectedColorPropertyName = "Bounding Shape.Deselected Color"; const char *activeHandleIdPropertyName = "Bounding Shape.Active Handle ID"; const char *boundingShapePropertyName = "Bounding Shape"; namespace mitk { itkEventMacroDefinition(BoundingShapeInteractionEvent, itk::AnyEvent); class BoundingShapeInteractor::Impl { public: Impl() : OriginalInteractionEnabled(false), RotationEnabled(false) { Point3D initialPoint; initialPoint.Fill(0.0); for (int i = 0; i < 6; ++i) Handles.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } ~Impl() {} bool OriginalInteractionEnabled; Point3D InitialPickedWorldPoint; Point3D LastPickedWorldPoint; Point2D InitialPickedDisplayPoint; std::vector<Handle> Handles; Handle ActiveHandle; Geometry3D::Pointer OriginalGeometry; bool RotationEnabled; std::map<us::ServiceReferenceU, mitk::EventConfig> DisplayInteractionConfigs; }; } mitk::BoundingShapeInteractor::BoundingShapeInteractor() : m_Impl(new Impl) { } mitk::BoundingShapeInteractor::~BoundingShapeInteractor() { this->RestoreNodeProperties(); delete m_Impl; } void mitk::BoundingShapeInteractor::ConnectActionsAndFunctions() { // **Conditions** that can be used in the state machine, to ensure that certain conditions are met, before actually // executing an action CONNECT_CONDITION("isHoveringOverObject", CheckOverObject); CONNECT_CONDITION("isHoveringOverHandles", CheckOverHandles); // **Function** in the statemachine patterns also referred to as **Actions** CONNECT_FUNCTION("selectObject", SelectObject); CONNECT_FUNCTION("deselectObject", DeselectObject); CONNECT_FUNCTION("deselectHandles", DeselectHandles); CONNECT_FUNCTION("initInteraction", InitInteraction); CONNECT_FUNCTION("translateObject", TranslateObject); CONNECT_FUNCTION("selectHandle", SelectHandle); CONNECT_FUNCTION("scaleObject", ScaleObject); // CONNECT_FUNCTION("rotateObject",RotateObject); } // RotateObject(StateMachineAction*, InteractionEvent* interactionEvent) // void mitk::BoundingShapeInteractor::RotateGeometry(mitk::ScalarType angle, int rotationaxis, mitk::BaseGeometry* // geometry) //{ // mitk::Vector3D rotationAxis = geometry->GetAxisVector(rotationaxis); // float pointX = 0.0f; // float pointY = 0.0f; // float pointZ = 0.0f; // mitk::Point3D pointOfRotation; // pointOfRotation.Fill(0.0); // this->GetDataNode()->GetFloatProperty(anchorPointX, pointX); // this->GetDataNode()->GetFloatProperty(anchorPointY, pointY); // this->GetDataNode()->GetFloatProperty(anchorPointZ, pointZ); // pointOfRotation[0] = pointX; // pointOfRotation[1] = pointY; // pointOfRotation[2] = pointZ; // // mitk::RotationOperation* doOp = new mitk::RotationOperation(OpROTATE, pointOfRotation, rotationAxis, angle); // // geometry->ExecuteOperation(doOp); // delete doOp; //} void mitk::BoundingShapeInteractor::SetRotationEnabled(bool rotationEnabled) { m_Impl->RotationEnabled = rotationEnabled; } void mitk::BoundingShapeInteractor::DataNodeChanged() { mitk::DataNode::Pointer newInputNode = this->GetDataNode(); if (newInputNode == nullptr) return; // add color properties mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(selectedColorPropertyName)); mitk::ColorProperty::Pointer deselectedColor = dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNull()) newInputNode->AddProperty(selectedColorPropertyName, mitk::ColorProperty::New(0.0, 1.0, 0.0)); if (deselectedColor.IsNull()) - newInputNode->AddProperty(deselectedColorPropertyName, mitk::ColorProperty::New(1.0, 1.0, 1.0)); + newInputNode->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)); newInputNode->SetProperty("layer", mitk::IntProperty::New(101)); newInputNode->SetBoolProperty("fixedLayer", mitk::BoolProperty::New(true)); newInputNode->SetBoolProperty("pickable", true); mitk::ColorProperty::Pointer initialColor = dynamic_cast<mitk::ColorProperty *>(newInputNode->GetProperty(deselectedColorPropertyName)); if (initialColor.IsNotNull()) { newInputNode->SetColor(initialColor->GetColor()); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::HandlePositionChanged(const InteractionEvent *interactionEvent, Point3D ¢er) { GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); if (m_Impl->Handles.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->Handles[0].SetPosition(pointLeft); m_Impl->Handles[1].SetPosition(pointRight); m_Impl->Handles[2].SetPosition(pointTop); m_Impl->Handles[3].SetPosition(pointBottom); m_Impl->Handles[4].SetPosition(pointFront); m_Impl->Handles[5].SetPosition(pointBack); // calculate center based on half way of the distance between two opposing cornerpoints center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); } } void mitk::BoundingShapeInteractor::SetDataNode(DataNode *node) { this->RestoreNodeProperties(); // if there is another node set, restore it's color if (node == nullptr) return; DataInteractor::SetDataNode(node); // calls DataNodeChanged internally this->DataNodeChanged(); } bool mitk::BoundingShapeInteractor::CheckOverObject(const InteractionEvent *interactionEvent) { const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return false; GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData()); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); // calculates translation based on offset+extent not on the transformation matrix (because the cube is located in the // center not in the origin) vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix(); Point3D center = geometry->GetCenter(); auto translation = vtkSmartPointer<vtkTransform>::New(); auto transform = vtkSmartPointer<vtkTransform>::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); mitk::Vector3D extent; for (unsigned int i = 0; i < 3; ++i) extent[i] = (geometry->GetExtent(i)); Point3D currentWorldPosition; Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); interactionEvent->GetSender()->DisplayToWorld(currentDisplayPosition, currentWorldPosition); ScalarType transformedPosition[4]; transformedPosition[0] = currentWorldPosition[0]; transformedPosition[1] = currentWorldPosition[1]; transformedPosition[2] = currentWorldPosition[2]; transformedPosition[3] = 1; // transform point from world to object coordinates transform->GetInverse()->TransformPoint(transformedPosition, transformedPosition); // check if the world point is within bounds bool isInside = (transformedPosition[0] >= (-extent[0] / 2.0)) && (transformedPosition[0] <= (extent[0] / 2.0)) && (transformedPosition[1] >= (-extent[1] / 2.0)) && (transformedPosition[1] <= (extent[1] / 2.0)) && (transformedPosition[2] >= (-extent[2] / 2.0)) && (transformedPosition[2] <= (extent[2] / 2.0)); return isInside; } bool mitk::BoundingShapeInteractor::CheckOverHandles(const InteractionEvent *interactionEvent) { Point3D boundingBoxCenter; HandlePositionChanged(interactionEvent, boundingBoxCenter); const auto *positionEvent = dynamic_cast<const InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return false; Point2D displayCenterPoint; // to do: change to actual time step (currently not necessary because geometry remains the same for each timestep int timeStep = 0; GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData()); BaseGeometry::Pointer geometry = geometryData->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); interactionEvent->GetSender()->WorldToDisplay(boundingBoxCenter, displayCenterPoint); double scale = interactionEvent->GetSender()->GetScaleFactorMMPerDisplayUnit(); // GetDisplaySizeInMM mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; if (handleSizeProperty != nullptr) initialHandleSize = handleSizeProperty->GetValue(); else initialHandleSize = 1.0 / 40.0; mitk::Point2D displaysize = interactionEvent->GetSender()->GetDisplaySizeInMM(); ScalarType handlesize = ((displaysize[0] + displaysize[1]) / 2.0) * initialHandleSize; unsigned int handleNum = 0; for (auto &handle : m_Impl->Handles) { Point2D centerpoint; interactionEvent->GetSender()->WorldToDisplay(handle.GetPosition(), centerpoint); Point2D currentDisplayPosition = positionEvent->GetPointerPositionOnScreen(); if ((currentDisplayPosition.EuclideanDistanceTo(centerpoint) < (handlesize / scale)) && (currentDisplayPosition.EuclideanDistanceTo(displayCenterPoint) > (handlesize / scale))) // check if mouse is hovering over center point { handle.SetActive(true); m_Impl->ActiveHandle = handle; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(handleNum++)); this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return true; } else { handleNum++; handle.SetActive(false); } this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); } return false; } void mitk::BoundingShapeInteractor::SelectHandle(StateMachineAction *, InteractionEvent *) { this->DisableOriginalInteraction(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(node->GetProperty(deselectedColorPropertyName)); if (selectedColor.IsNotNull()) { this->GetDataNode()->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectHandles(StateMachineAction *, InteractionEvent *) { this->DisableOriginalInteraction(); DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; this->GetDataNode()->GetPropertyList()->SetProperty(activeHandleIdPropertyName, mitk::IntProperty::New(-1)); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::SelectObject(StateMachineAction *, InteractionEvent *) { this->DisableOriginalInteraction(); // disable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(node->GetProperty(selectedColorPropertyName)); if (selectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", selectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::DeselectObject(StateMachineAction *, InteractionEvent *) { this->EnableOriginalInteraction(); // enable crosshair interaction and scolling if user is hovering over the object DataNode::Pointer node = this->GetDataNode(); if (node.IsNull()) return; mitk::ColorProperty::Pointer deselectedColor = dynamic_cast<mitk::ColorProperty *>(node->GetProperty(deselectedColorPropertyName)); if (deselectedColor.IsNotNull()) { node->GetPropertyList()->SetProperty("color", deselectedColor); } this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); RenderingManager::GetInstance()->RequestUpdateAll(); return; } void mitk::BoundingShapeInteractor::InitInteraction(StateMachineAction *, InteractionEvent *interactionEvent) { InitMembers(interactionEvent); } bool mitk::BoundingShapeInteractor::InitMembers(InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return false; // get initial position coordinates m_Impl->InitialPickedDisplayPoint = positionEvent->GetPointerPositionOnScreen(); m_Impl->InitialPickedWorldPoint = positionEvent->GetPositionInWorld(); m_Impl->LastPickedWorldPoint = positionEvent->GetPositionInWorld(); return true; } void mitk::BoundingShapeInteractor::TranslateObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return; int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = this->GetDataNode()->GetData()->GetUpdatedTimeGeometry()->GetGeometryForTimeStep(timeStep); Vector3D spacing = geometry->GetSpacing(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); Vector3D interactionMove; // pixel aligned shifting of the bounding box interactionMove[0] = std::round((currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]) / spacing[0]) * spacing[0]; interactionMove[1] = std::round((currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]) / spacing[1]) * spacing[1]; interactionMove[2] = std::round((currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]) / spacing[2]) * spacing[2]; if ((interactionMove[0] + interactionMove[1] + interactionMove[2]) != 0.0) // only update current position if a movement occured { m_Impl->LastPickedWorldPoint = currentPickedPoint; geometry->SetOrigin(geometry->GetOrigin() + interactionMove); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } return; } void mitk::BoundingShapeInteractor::ScaleObject(StateMachineAction *, InteractionEvent *interactionEvent) { auto *positionEvent = dynamic_cast<InteractionPositionEvent *>(interactionEvent); if (positionEvent == nullptr) return; GeometryData::Pointer geometryData = dynamic_cast<GeometryData *>(this->GetDataNode()->GetData()); Point3D handlePickedPoint = m_Impl->ActiveHandle.GetPosition(); Point3D currentPickedPoint; interactionEvent->GetSender()->DisplayToWorld(positionEvent->GetPointerPositionOnScreen(), currentPickedPoint); int timeStep = interactionEvent->GetSender()->GetTimeStep(this->GetDataNode()->GetData()); mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(timeStep); Vector3D spacing = geometry->GetSpacing(); // pixel aligned bounding box Vector3D interactionMove; interactionMove[0] = (currentPickedPoint[0] - m_Impl->LastPickedWorldPoint[0]); interactionMove[1] = (currentPickedPoint[1] - m_Impl->LastPickedWorldPoint[1]); interactionMove[2] = (currentPickedPoint[2] - m_Impl->LastPickedWorldPoint[2]); std::vector<int> faces = m_Impl->ActiveHandle.GetFaceIndices(); auto pointscontainer = mitk::BoundingBox::PointsContainer::New(); // calculate cornerpoints from geometry plus visualization offset std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); unsigned int num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); Vector3D faceNormal; faceNormal[0] = handlePickedPoint[0] - center[0]; faceNormal[1] = handlePickedPoint[1] - center[1]; faceNormal[2] = handlePickedPoint[2] - center[2]; Vector3D faceShift = ((faceNormal * interactionMove) / (faceNormal.GetNorm() * faceNormal.GetNorm())) * faceNormal; // calculate cornerpoints from geometry without visualization offset to update actual geometry cornerPoints = GetCornerPoints(geometry, false); num = 0; for (const auto &point : cornerPoints) { pointscontainer->InsertElement(num++, point); } bool positionChangeThreshold = true; for (int numFaces = 0; numFaces < 8; numFaces++) // estimate the corresponding face and shift its assigned points { if ((numFaces != faces[0]) && (numFaces != faces[1]) && (numFaces != faces[2]) && (numFaces != faces[3])) { Point3D point = pointscontainer->GetElement(numFaces); if (m_Impl->RotationEnabled) // apply if geometry is rotated and a pixel aligned shift is not possible { point[0] += faceShift[0]; point[1] += faceShift[1]; point[2] += faceShift[2]; } else // shift pixelwise { point[0] += std::round(faceShift[0] / spacing[0]) * spacing[0]; point[1] += std::round(faceShift[1] / spacing[1]) * spacing[1]; point[2] += std::round(faceShift[2] / spacing[2]) * spacing[2]; } if (point == pointscontainer->GetElement(numFaces)) positionChangeThreshold = false; else m_Impl->LastPickedWorldPoint = point; pointscontainer->InsertElement(numFaces, point); } } if (positionChangeThreshold) // update only if bounding box is shifted at least by one pixel { auto inverse = mitk::AffineTransform3D::New(); geometry->GetIndexToWorldTransform()->GetInverse(inverse); for (unsigned int pointid = 0; pointid < 8; pointid++) { pointscontainer->InsertElement(pointid, inverse->TransformPoint(pointscontainer->GetElement(pointid))); } auto bbox = mitk::BoundingBox::New(); bbox->SetPoints(pointscontainer); bbox->ComputeBoundingBox(); mitk::Point3D BBmin = bbox->GetMinimum(); mitk::Point3D BBmax = bbox->GetMaximum(); if (std::abs(BBmin[0] - BBmax[0]) > 0.01 && std::abs(BBmin[1] - BBmax[1]) > 0.01 && std::abs(BBmin[2] - BBmax[2]) > 0.01) // TODO: check if the extent is greater than zero { geometry->SetBounds(bbox->GetBounds()); geometry->Modified(); this->GetDataNode()->GetData()->UpdateOutputInformation(); // Geometry is up-to-date this->GetDataNode()->GetData()->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } return; } void mitk::BoundingShapeInteractor::RestoreNodeProperties() { mitk::DataNode::Pointer inputNode = this->GetDataNode(); if (inputNode.IsNull()) return; mitk::ColorProperty::Pointer color = (mitk::ColorProperty::New(1.0, 1.0, 1.0)); if (color.IsNotNull()) { inputNode->GetPropertyList()->SetProperty("color", color); } inputNode->SetProperty("layer", mitk::IntProperty::New(99)); inputNode->SetProperty(boundingShapePropertyName, mitk::BoolProperty::New(false)); inputNode->GetPropertyList()->DeleteProperty(activeHandleIdPropertyName); EnableOriginalInteraction(); // update rendering mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::BoundingShapeInteractor::EnableOriginalInteraction() { // Re-enabling InteractionEventObservers that have been previously disabled for legacy handling of Tools // in new interaction framework for (const auto& displayInteractionConfig : m_Impl->DisplayInteractionConfigs) { if (displayInteractionConfig.first) { auto displayActionEventBroadcast = static_cast<mitk::DisplayActionEventBroadcast *>( us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(displayInteractionConfig.first)); if (nullptr != displayActionEventBroadcast) { // here the regular configuration is loaded again displayActionEventBroadcast->SetEventConfig(displayInteractionConfig.second); } } } m_Impl->DisplayInteractionConfigs.clear(); m_Impl->OriginalInteractionEnabled = true; } void mitk::BoundingShapeInteractor::DisableOriginalInteraction() { // dont deactivate twice, else we will clutter the config list ... if (false == m_Impl->OriginalInteractionEnabled) return; // As a legacy solution the display interaction of the new interaction framework is disabled here to avoid conflicts // with tools // Note: this only affects InteractionEventObservers (formerly known as Listeners) all DataNode specific interaction // will still be enabled m_Impl->DisplayInteractionConfigs.clear(); auto eventObservers = us::GetModuleContext()->GetServiceReferences<mitk::InteractionEventObserver>(); for (const auto& eventObserver : eventObservers) { auto *displayActionEventBroadcast = dynamic_cast<mitk::DisplayActionEventBroadcast *>( us::GetModuleContext()->GetService<mitk::InteractionEventObserver>(eventObserver)); if (nullptr != displayActionEventBroadcast) { // remember the original configuration m_Impl->DisplayInteractionConfigs.insert(std::make_pair(eventObserver, displayActionEventBroadcast->GetEventConfig())); // here the alternative configuration is loaded displayActionEventBroadcast->AddEventConfig("DisplayConfigBlockLMB.xml"); } } m_Impl->OriginalInteractionEnabled = false; } diff --git a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp index 4d6afbb5a3..5114a48201 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper2D.cpp @@ -1,467 +1,462 @@ /*============================================================================ 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 "../DataManagement/mitkBoundingShapeUtil.h" #include <mitkBaseProperty.h> #include <mitkBoundingShapeVtkMapper2D.h> #include <vtkActor2D.h> #include <vtkAppendPolyData.h> #include <vtkCoordinate.h> -#include <vtkCubeSource.h> #include <vtkMath.h> #include <vtkPointData.h> #include <vtkPolyData.h> #include <vtkPolyDataMapper2D.h> #include <vtkProperty2D.h> -#include <vtkSphereSource.h> #include <vtkStripper.h> #include <vtkTransformFilter.h> #include <vtkTransformPolyDataFilter.h> -static vtkSmartPointer<vtkSphereSource> CreateHandle() -{ - auto handle = vtkSmartPointer<vtkSphereSource>::New(); - - handle->SetPhiResolution(8); - handle->SetThetaResolution(16); - - return handle; -} - namespace mitk { class BoundingShapeVtkMapper2D::Impl { public: Impl() { Point3D initialPoint; initialPoint.Fill(0); for (int i = 0; i < 6; ++i) HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } std::vector<Handle> HandlePropertyList; mitk::LocalStorageHandler<LocalStorage> LocalStorageHandler; }; } mitk::BoundingShapeVtkMapper2D::LocalStorage::LocalStorage() : m_Actor(vtkSmartPointer<vtkActor>::New()), m_HandleActor(vtkSmartPointer<vtkActor2D>::New()), m_SelectedHandleActor(vtkSmartPointer<vtkActor2D>::New()), m_Mapper(vtkSmartPointer<vtkPolyDataMapper>::New()), m_HandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()), m_SelectedHandleMapper(vtkSmartPointer<vtkPolyDataMapper2D>::New()), m_Cutter(vtkSmartPointer<vtkCutter>::New()), m_CuttingPlane(vtkSmartPointer<vtkPlane>::New()), m_LastSliceNumber(0), m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::New()), m_ZoomFactor(1.0) { m_Actor->SetMapper(m_Mapper); - m_Actor->GetProperty()->SetOpacity(0.3); m_Actor->VisibilityOn(); m_HandleActor->SetMapper(m_HandleMapper); m_HandleActor->VisibilityOn(); m_SelectedHandleActor->VisibilityOn(); m_SelectedHandleActor->GetProperty()->SetColor(0, 1.0, 0); m_SelectedHandleActor->SetMapper(m_SelectedHandleMapper); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); m_SelectedHandleMapper->SetTransformCoordinate(tcoord); tcoord->Delete(); m_Cutter->SetCutFunction(m_CuttingPlane); for (int i = 0; i < 6; ++i) - m_Handles.push_back(CreateHandle()); + m_Handles.push_back(vtkSmartPointer<vtkCubeSource>::New()); m_PropAssembly->AddPart(m_Actor); m_PropAssembly->AddPart(m_HandleActor); m_PropAssembly->VisibilityOn(); } bool mitk::BoundingShapeVtkMapper2D::LocalStorage::IsUpdateRequired(mitk::BaseRenderer *renderer, mitk::Mapper *mapper, mitk::DataNode *dataNode) { const mitk::PlaneGeometry *worldGeometry = renderer->GetCurrentWorldPlaneGeometry(); if (m_LastGenerateDataTime < worldGeometry->GetMTime()) return true; unsigned int sliceNumber = renderer->GetSlice(); if (m_LastSliceNumber != sliceNumber) return true; if (mapper && m_LastGenerateDataTime < mapper->GetMTime()) return true; if (dataNode) { if (m_LastGenerateDataTime < dataNode->GetMTime()) return true; mitk::BaseData *data = dataNode->GetData(); if (data && m_LastGenerateDataTime < data->GetMTime()) return true; } return false; } mitk::BoundingShapeVtkMapper2D::LocalStorage::~LocalStorage() { } void mitk::BoundingShapeVtkMapper2D::Update(mitk::BaseRenderer *renderer) { this->GenerateDataForRenderer(renderer); } void mitk::BoundingShapeVtkMapper2D::SetDefaultProperties(DataNode *node, BaseRenderer *renderer, bool overwrite) { Superclass::SetDefaultProperties(node, renderer, overwrite); + node->AddProperty("opacity", FloatProperty::New(0.2f), renderer, overwrite); } mitk::BoundingShapeVtkMapper2D::BoundingShapeVtkMapper2D() : m_Impl(new Impl) { } mitk::BoundingShapeVtkMapper2D::~BoundingShapeVtkMapper2D() { delete m_Impl; } void mitk::BoundingShapeVtkMapper2D::GenerateDataForRenderer(BaseRenderer *renderer) { const DataNode::Pointer node = GetDataNode(); if (node == nullptr) return; LocalStorage *localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer); // either update if GeometryData was modified or if the zooming was performed bool needGenerateData = localStorage->IsUpdateRequired( renderer, this, GetDataNode()); // true; // localStorage->GetLastGenerateDataTime() < node->GetMTime() || // localStorage->GetLastGenerateDataTime() < node->GetData()->GetMTime(); // //localStorage->IsGenerateDataRequired(renderer, this, GetDataNode()); double scale = renderer->GetScaleFactorMMPerDisplayUnit(); if (std::abs(scale - localStorage->m_ZoomFactor) > 0.001) { localStorage->m_ZoomFactor = scale; needGenerateData = true; } if (needGenerateData) { bool visible = true; GetDataNode()->GetVisibility(visible, renderer, "visible"); if (!visible) { localStorage->m_Actor->VisibilityOff(); return; } GeometryData::Pointer shape = static_cast<GeometryData *>(node->GetData()); if (shape == nullptr) return; mitk::BaseGeometry::Pointer geometry = shape->GetGeometry(); mitk::Vector3D spacing = geometry->GetSpacing(); // calculate cornerpoints and extent from geometry with visualization offset std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); Point3D p0 = cornerPoints[0]; Point3D p1 = cornerPoints[1]; Point3D p2 = cornerPoints[2]; Point3D p4 = cornerPoints[4]; Point3D extent; extent[0] = sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2])); extent[1] = sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2])); extent[2] = sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2])); // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); if (m_Impl->HandlePropertyList.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->HandlePropertyList[0].SetPosition(pointLeft); m_Impl->HandlePropertyList[1].SetPosition(pointRight); m_Impl->HandlePropertyList[2].SetPosition(pointTop); m_Impl->HandlePropertyList[3].SetPosition(pointBottom); m_Impl->HandlePropertyList[4].SetPosition(pointFront); m_Impl->HandlePropertyList[5].SetPosition(pointBack); } // caculate face normals double cubeFaceNormal0[3], cubeFaceNormal1[3], cubeFaceNormal2[3]; double a[3], b[3]; a[0] = (cornerPoints[5][0] - cornerPoints[6][0]); a[1] = (cornerPoints[5][1] - cornerPoints[6][1]); a[2] = (cornerPoints[5][2] - cornerPoints[6][2]); b[0] = (cornerPoints[5][0] - cornerPoints[4][0]); b[1] = (cornerPoints[5][1] - cornerPoints[4][1]); b[2] = (cornerPoints[5][2] - cornerPoints[4][2]); vtkMath::Cross(a, b, cubeFaceNormal0); a[0] = (cornerPoints[0][0] - cornerPoints[6][0]); a[1] = (cornerPoints[0][1] - cornerPoints[6][1]); a[2] = (cornerPoints[0][2] - cornerPoints[6][2]); b[0] = (cornerPoints[0][0] - cornerPoints[2][0]); b[1] = (cornerPoints[0][1] - cornerPoints[2][1]); b[2] = (cornerPoints[0][2] - cornerPoints[2][2]); vtkMath::Cross(a, b, cubeFaceNormal1); a[0] = (cornerPoints[2][0] - cornerPoints[7][0]); a[1] = (cornerPoints[2][1] - cornerPoints[7][1]); a[2] = (cornerPoints[2][2] - cornerPoints[7][2]); b[0] = (cornerPoints[2][0] - cornerPoints[6][0]); b[1] = (cornerPoints[2][1] - cornerPoints[6][1]); b[2] = (cornerPoints[2][2] - cornerPoints[6][2]); vtkMath::Cross(a, b, cubeFaceNormal2); vtkMath::Normalize(cubeFaceNormal0); vtkMath::Normalize(cubeFaceNormal1); vtkMath::Normalize(cubeFaceNormal2); // create cube for rendering bounding box auto cube = vtkCubeSource::New(); cube->SetXLength(extent[0] / spacing[0]); cube->SetYLength(extent[1] / spacing[1]); cube->SetZLength(extent[2] / spacing[2]); // calculates translation based on offset+extent not on the transformation matrix vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix(); auto translation = vtkSmartPointer<vtkTransform>::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer<vtkTransform>::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); cube->Update(); auto transformFilter = vtkSmartPointer<vtkTransformFilter>::New(); transformFilter->SetInputData(cube->GetOutput()); transformFilter->SetTransform(transform); transformFilter->Update(); cube->Delete(); vtkSmartPointer<vtkPolyData> polydata = transformFilter->GetPolyDataOutput(); if (polydata == nullptr || (polydata->GetNumberOfPoints() < 1)) { localStorage->m_Actor->VisibilityOff(); localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); return; } // estimate current image plane to decide whether the cube is visible or not const PlaneGeometry *planeGeometry = renderer->GetCurrentWorldPlaneGeometry(); if ((planeGeometry == nullptr) || (!planeGeometry->IsValid()) || (!planeGeometry->HasReferenceGeometry())) return; double origin[3]; origin[0] = planeGeometry->GetOrigin()[0]; origin[1] = planeGeometry->GetOrigin()[1]; origin[2] = planeGeometry->GetOrigin()[2]; double displayPlaneNormal[3]; displayPlaneNormal[0] = planeGeometry->GetNormal()[0]; displayPlaneNormal[1] = planeGeometry->GetNormal()[1]; displayPlaneNormal[2] = planeGeometry->GetNormal()[2]; vtkMath::Normalize(displayPlaneNormal); localStorage->m_CuttingPlane->SetOrigin(origin); localStorage->m_CuttingPlane->SetNormal(displayPlaneNormal); // add cube polydata to local storage localStorage->m_Cutter->SetInputData(polydata); localStorage->m_Cutter->SetGenerateCutScalars(1); localStorage->m_Cutter->Update(); if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_HandleActor)) localStorage->m_PropAssembly->RemovePart(localStorage->m_HandleActor); if (localStorage->m_PropAssembly->GetParts()->IsItemPresent(localStorage->m_Actor)) localStorage->m_PropAssembly->RemovePart(localStorage->m_Actor); vtkCoordinate *tcoord = vtkCoordinate::New(); tcoord->SetCoordinateSystemToWorld(); localStorage->m_HandleMapper->SetTransformCoordinate(tcoord); tcoord->Delete(); if (localStorage->m_Cutter->GetOutput()->GetNumberOfPoints() > 0) // if plane is visible in the renderwindow { mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; 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; auto appendPoly = vtkSmartPointer<vtkAppendPolyData>::New(); unsigned int handleIdx = 0; // add handles and their assigned properties to the local storage mitk::IntProperty::Pointer activeHandleId = dynamic_cast<mitk::IntProperty *>(node->GetProperty("Bounding Shape.Active Handle ID")); double angle0 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal0))); if (angle0 > 179.0) angle0 -= 180.0; double angle1 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal1))); if (angle1 > 179.0) angle1 -= 180.0; double angle2 = std::abs(vtkMath::DegreesFromRadians(vtkMath::AngleBetweenVectors(displayPlaneNormal, cubeFaceNormal2))); if (angle2 > 179.0) angle2 -= 180.0; bool visible = false; bool selected = false; for (auto& handle : localStorage->m_Handles) { 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 if ( (handleIdx != 0 && handleIdx != 1 && std::abs(angle0) < 0.1) || // handles 0 and 1 (handleIdx != 2 && handleIdx != 3 && std::abs(angle1) < 0.1) || // handles 2 and 3 (handleIdx != 4 && handleIdx != 5 && std::abs(angle2) < 0.1) ) // handles 4 and 5 { if (activeHandleId == nullptr) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { if ((activeHandleId->GetValue() != m_Impl->HandlePropertyList[handleIdx].GetIndex())) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { handle->Update(); localStorage->m_SelectedHandleMapper->SetInputData(handle->GetOutput()); localStorage->m_SelectedHandleActor->VisibilityOn(); selected = true; } } visible = true; } ++handleIdx; } if (visible) { appendPoly->Update(); } else { localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); } auto stripper = vtkSmartPointer<vtkStripper>::New(); stripper->SetInputData(localStorage->m_Cutter->GetOutput()); stripper->Update(); auto cutPolyData = vtkSmartPointer<vtkPolyData>::New(); cutPolyData->SetPoints(stripper->GetOutput()->GetPoints()); cutPolyData->SetPolys(stripper->GetOutput()->GetLines()); localStorage->m_Actor->GetMapper()->SetInputDataObject(cutPolyData); - mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(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) { localStorage->m_HandleActor->GetProperty()->SetColor(1, 0, 0); } else { localStorage->m_HandleActor->GetProperty()->SetColor(1, 1, 1); } localStorage->m_HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput()); // add parts to the overall storage localStorage->m_PropAssembly->AddPart(localStorage->m_Actor); localStorage->m_PropAssembly->AddPart(localStorage->m_HandleActor); if (selected) { localStorage->m_PropAssembly->AddPart(localStorage->m_SelectedHandleActor); } localStorage->m_PropAssembly->VisibilityOn(); localStorage->m_Actor->VisibilityOn(); localStorage->m_HandleActor->VisibilityOn(); } else { localStorage->m_PropAssembly->VisibilityOff(); localStorage->m_Actor->VisibilityOff(); localStorage->m_HandleActor->VisibilityOff(); localStorage->m_SelectedHandleActor->VisibilityOff(); localStorage->UpdateGenerateDataTime(); } localStorage->UpdateGenerateDataTime(); } } vtkProp *mitk::BoundingShapeVtkMapper2D::GetVtkProp(BaseRenderer *renderer) { return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->m_PropAssembly; } -void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *, vtkActor *) +void mitk::BoundingShapeVtkMapper2D::ApplyColorAndOpacityProperties(BaseRenderer *renderer, vtkActor *actor) { + auto* property = actor->GetProperty(); + + std::array<float, 3> 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 75d303f210..b07e626fa2 100644 --- a/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp +++ b/Modules/BoundingShape/src/Rendering/mitkBoundingShapeVtkMapper3D.cpp @@ -1,335 +1,333 @@ /*============================================================================ 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 "mitkBoundingShapeVtkMapper3D.h" #include "../DataManagement/mitkBoundingShapeUtil.h" #include <mitkBaseProperty.h> #include <vtkAppendPolyData.h> #include <vtkCamera.h> #include <vtkCubeSource.h> #include <vtkDataSetMapper.h> #include <vtkMath.h> #include <vtkPolyData.h> #include <vtkPolyDataMapper.h> -#include <vtkSphereSource.h> #include <vtkTransformFilter.h> namespace mitk { class BoundingShapeVtkMapper3D::Impl { class LocalStorage : public Mapper::BaseLocalStorage { public: LocalStorage(); ~LocalStorage() override; LocalStorage(const LocalStorage &) = delete; LocalStorage &operator=(const LocalStorage &) = delete; - std::vector<vtkSmartPointer<vtkSphereSource>> Handles; + std::vector<vtkSmartPointer<vtkCubeSource>> Handles; vtkSmartPointer<vtkActor> Actor; vtkSmartPointer<vtkActor> HandleActor; vtkSmartPointer<vtkActor> SelectedHandleActor; vtkSmartPointer<vtkPropAssembly> PropAssembly; }; public: Impl() : DistanceFromCam(1.0) { Point3D initialPoint; initialPoint.Fill(0); for (int i = 0; i < 6; ++i) HandlePropertyList.push_back(Handle(initialPoint, i, GetHandleIndices(i))); } double DistanceFromCam; std::vector<Handle> HandlePropertyList; mitk::LocalStorageHandler<LocalStorage> LocalStorageHandler; }; } mitk::BoundingShapeVtkMapper3D::Impl::LocalStorage::LocalStorage() : Actor(vtkSmartPointer<vtkActor>::New()), HandleActor(vtkSmartPointer<vtkActor>::New()), SelectedHandleActor(vtkSmartPointer<vtkActor>::New()), PropAssembly(vtkSmartPointer<vtkPropAssembly>::New()) { for (int i = 0; i < 6; i++) - Handles.push_back(vtkSmartPointer<vtkSphereSource>::New()); + Handles.push_back(vtkSmartPointer<vtkCubeSource>::New()); } 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) { } 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<float, 3> 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) { if (actor == nullptr) return; auto dataNode = this->GetDataNode(); if (dataNode == nullptr) return; bool isVisible = false; dataNode->GetBoolProperty("Bounding Shape.3D Rendering", isVisible, renderer); actor->SetVisibility(isVisible); float lineWidth = 1.0f; dataNode->GetFloatProperty("Bounding Shape.Line.Width", lineWidth, renderer); auto property = actor->GetProperty(); property->SetLineWidth(lineWidth); } void mitk::BoundingShapeVtkMapper3D::GenerateDataForRenderer(BaseRenderer *renderer) { auto dataNode = this->GetDataNode(); if (dataNode == nullptr) return; vtkCamera *camera = renderer->GetVtkRenderer()->GetActiveCamera(); auto localStorage = m_Impl->LocalStorageHandler.GetLocalStorage(renderer); bool needGenerateData = localStorage->GetLastGenerateDataTime() < dataNode->GetMTime(); double distance = camera->GetDistance(); if (std::abs(distance - m_Impl->DistanceFromCam) > mitk::eps) { m_Impl->DistanceFromCam = distance; needGenerateData = true; } if (needGenerateData) { bool isVisible = true; dataNode->GetVisibility(isVisible, renderer); if (!isVisible) { localStorage->Actor->VisibilityOff(); return; } // set the input-object at time t for the mapper auto *geometryData = dynamic_cast<GeometryData *>(dataNode->GetData()); if (geometryData == nullptr) return; mitk::BaseGeometry::Pointer geometry = geometryData->GetGeometry(); mitk::Vector3D spacing = geometry->GetSpacing(); // calculate cornerpoints from geometry std::vector<Point3D> cornerPoints = GetCornerPoints(geometry, true); Point3D p0 = cornerPoints[0]; Point3D p1 = cornerPoints[1]; Point3D p2 = cornerPoints[2]; Point3D p4 = cornerPoints[4]; Point3D extent; extent[0] = sqrt((p0[0] - p4[0]) * (p0[0] - p4[0]) + (p0[1] - p4[1]) * (p0[1] - p4[1]) + (p0[2] - p4[2]) * (p0[2] - p4[2])); extent[1] = sqrt((p0[0] - p2[0]) * (p0[0] - p2[0]) + (p0[1] - p2[1]) * (p0[1] - p2[1]) + (p0[2] - p2[2]) * (p0[2] - p2[2])); extent[2] = sqrt((p0[0] - p1[0]) * (p0[0] - p1[0]) + (p0[1] - p1[1]) * (p0[1] - p1[1]) + (p0[2] - p1[2]) * (p0[2] - p1[2])); // calculate center based on half way of the distance between two opposing cornerpoints mitk::Point3D center = CalcAvgPoint(cornerPoints[7], cornerPoints[0]); if (m_Impl->HandlePropertyList.size() == 6) { // set handle positions Point3D pointLeft = CalcAvgPoint(cornerPoints[5], cornerPoints[6]); Point3D pointRight = CalcAvgPoint(cornerPoints[1], cornerPoints[2]); Point3D pointTop = CalcAvgPoint(cornerPoints[0], cornerPoints[6]); Point3D pointBottom = CalcAvgPoint(cornerPoints[7], cornerPoints[1]); Point3D pointFront = CalcAvgPoint(cornerPoints[2], cornerPoints[7]); Point3D pointBack = CalcAvgPoint(cornerPoints[4], cornerPoints[1]); m_Impl->HandlePropertyList[0].SetPosition(pointLeft); m_Impl->HandlePropertyList[1].SetPosition(pointRight); m_Impl->HandlePropertyList[2].SetPosition(pointTop); m_Impl->HandlePropertyList[3].SetPosition(pointBottom); m_Impl->HandlePropertyList[4].SetPosition(pointFront); m_Impl->HandlePropertyList[5].SetPosition(pointBack); } auto cube = vtkCubeSource::New(); cube->SetXLength(extent[0] / spacing[0]); cube->SetYLength(extent[1] / spacing[1]); cube->SetZLength(extent[2] / spacing[2]); // calculates translation based on offset+extent not on the transformation matrix vtkSmartPointer<vtkMatrix4x4> imageTransform = geometry->GetVtkTransform()->GetMatrix(); auto translation = vtkSmartPointer<vtkTransform>::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer<vtkTransform>::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); cube->Update(); auto transformFilter = vtkSmartPointer<vtkTransformFilter>::New(); transformFilter->SetInputData(cube->GetOutput()); transformFilter->SetTransform(transform); transformFilter->Update(); cube->Delete(); vtkSmartPointer<vtkPolyData> polydata = transformFilter->GetPolyDataOutput(); if (polydata == nullptr) { localStorage->Actor->VisibilityOff(); return; } mitk::DoubleProperty::Pointer handleSizeProperty = dynamic_cast<mitk::DoubleProperty *>(this->GetDataNode()->GetProperty("Bounding Shape.Handle Size Factor")); ScalarType initialHandleSize; 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) * initialHandleSize; if (localStorage->PropAssembly->GetParts()->IsItemPresent(localStorage->HandleActor)) localStorage->PropAssembly->RemovePart(localStorage->HandleActor); if (localStorage->PropAssembly->GetParts()->IsItemPresent(localStorage->Actor)) localStorage->PropAssembly->RemovePart(localStorage->Actor); auto selectedhandlemapper = vtkSmartPointer<vtkPolyDataMapper>::New(); auto appendPoly = vtkSmartPointer<vtkAppendPolyData>::New(); mitk::IntProperty::Pointer activeHandleId = dynamic_cast<mitk::IntProperty *>(dataNode->GetProperty("Bounding Shape.Active Handle ID")); int i = 0; for (auto &handle : localStorage->Handles) { 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) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { if (activeHandleId->GetValue() != m_Impl->HandlePropertyList[i].GetIndex()) { appendPoly->AddInputConnection(handle->GetOutputPort()); } else { selectedhandlemapper->SetInputData(handle->GetOutput()); localStorage->SelectedHandleActor->SetMapper(selectedhandlemapper); localStorage->SelectedHandleActor->GetProperty()->SetColor(0, 1, 0); localStorage->SelectedHandleActor->GetMapper()->SetInputDataObject(handle->GetOutput()); localStorage->PropAssembly->AddPart(localStorage->SelectedHandleActor); } } i++; } appendPoly->Update(); auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); mapper->SetInputData(polydata); auto handlemapper = vtkSmartPointer<vtkPolyDataMapper>::New(); handlemapper->SetInputData(appendPoly->GetOutput()); localStorage->Actor->SetMapper(mapper); localStorage->Actor->GetMapper()->SetInputDataObject(polydata); - localStorage->Actor->GetProperty()->SetOpacity(0.3); - - mitk::ColorProperty::Pointer selectedColor = dynamic_cast<mitk::ColorProperty *>(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) { localStorage->HandleActor->GetProperty()->SetColor(1, 1, 1); } else { localStorage->HandleActor->GetProperty()->SetColor(1, 0, 0); } localStorage->HandleActor->GetMapper()->SetInputDataObject(appendPoly->GetOutput()); 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 // this->ApplyProperties(localStorage->m_Actor, renderer); // this->ApplyProperties(localStorage->m_HandleActor, renderer); // this->ApplyProperties(localStorage->m_SelectedHandleActor, renderer); localStorage->Actor->VisibilityOn(); localStorage->HandleActor->VisibilityOn(); localStorage->SelectedHandleActor->VisibilityOn(); localStorage->PropAssembly->AddPart(localStorage->Actor); localStorage->PropAssembly->AddPart(localStorage->HandleActor); localStorage->PropAssembly->VisibilityOn(); localStorage->UpdateGenerateDataTime(); } } vtkProp *mitk::BoundingShapeVtkMapper3D::GetVtkProp(BaseRenderer *renderer) { return m_Impl->LocalStorageHandler.GetLocalStorage(renderer)->PropAssembly; } diff --git a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp index e6ac3712c0..1e4f18c97f 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp +++ b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.cpp @@ -1,59 +1,69 @@ /*============================================================================ 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 "mitkCameraIntrinsicsProperty.h" namespace mitk { bool CameraIntrinsicsProperty::IsEqual(const BaseProperty& property) const { return this->m_Value->Equals(static_cast<const Self&>(property).m_Value.GetPointer()); } bool CameraIntrinsicsProperty::Assign(const BaseProperty& property) { this->m_Value = static_cast<const Self&>(property).m_Value; return true; } std::string CameraIntrinsicsProperty::GetValueAsString() const { std::stringstream myStr; myStr << GetValue(); return myStr.str(); } CameraIntrinsicsProperty::CameraIntrinsicsProperty() : BaseProperty() {} CameraIntrinsicsProperty::CameraIntrinsicsProperty(const CameraIntrinsicsProperty& other) : BaseProperty(other) { } CameraIntrinsicsProperty::CameraIntrinsicsProperty( mitk::CameraIntrinsics::Pointer value ) : BaseProperty(), m_Value( value ) {} itk::LightObject::Pointer CameraIntrinsicsProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); 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 c992948156..23d40b6c69 100644 --- a/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h +++ b/Modules/CameraCalibration/mitkCameraIntrinsicsProperty.h @@ -1,73 +1,76 @@ /*============================================================================ 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 mitkCameraIntrinsicsProperty_h #define mitkCameraIntrinsicsProperty_h #include "mitkBaseProperty.h" #include "mitkCameraIntrinsics.h" namespace mitk { #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) #endif class MITKCAMERACALIBRATION_EXPORT CameraIntrinsicsProperty : public BaseProperty { public: typedef mitk::CameraIntrinsics::Pointer ValueType; mitkClassMacro(CameraIntrinsicsProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(CameraIntrinsicsProperty, mitk::CameraIntrinsics::Pointer); itkSetMacro(Value, mitk::CameraIntrinsics::Pointer ); itkGetConstMacro(Value, mitk::CameraIntrinsics::Pointer ); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: mitk::CameraIntrinsics::Pointer m_Value; CameraIntrinsicsProperty(); CameraIntrinsicsProperty(const CameraIntrinsicsProperty&); CameraIntrinsicsProperty( mitk::CameraIntrinsics::Pointer value ); private: // purposely not implemented CameraIntrinsicsProperty& operator=(const CameraIntrinsicsProperty&); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty& property) const override; bool Assign(const BaseProperty& property) override; }; #ifdef _MSC_VER # pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/CMakeLists.txt b/Modules/Core/CMakeLists.txt index 0ad7a726f5..74cada7281 100644 --- a/Modules/Core/CMakeLists.txt +++ b/Modules/Core/CMakeLists.txt @@ -1,74 +1,74 @@ set(TOOL_CPPS "") # temporary suppress warnings in the following files until image accessors are fully integrated. set_source_files_properties( src/DataManagement/mitkImage.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) set_source_files_properties( src/Controllers/mitkSliceNavigationController.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) mitk_create_module( INCLUDE_DIRS PUBLIC ${MITK_BINARY_DIR} PRIVATE src/Algorithms src/Controllers src/DataManagement src/Interactions src/IO src/Rendering DEPENDS PUBLIC MitkLog CppMicroServices 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. # Because the VTK "auto-init" functionality injects file-local static # initialization code in every cpp file which includes a VTK header, # static initialization order becomes an issue again. For the Mitk # core library, we need to ensure that the VTK static initialization stuff # happens before the CppMicroServices initialization, since the latter # might already use VTK code which needs to access VTK object factories. # Hence, CppMicroServices initialization code is placed manually within # the mitkCoreActivator.cpp file. NO_INIT ) if(NOT TARGET ${MODULE_TARGET}) message(SEND_ERROR "Core target ${MODULE_TARGET} does not exist") endif() function(_itk_create_factory_register_manager) # In MITK_ITK_Config.cmake, we do *not* include ITK_USE_FILE, which # prevents multiple registrations/unregistrations of ITK IO factories # during library loading/unloading (of MITK libraries). However, we need # "one" place where the IO factories are registered at # least once. This could be the application executable, but every executable would # need to take care of that itself. Instead, we allow the auto registration in the # Mitk Core library. set(NO_DIRECTORY_SCOPED_ITK_COMPILE_DEFINITION 1) find_package(ITK) include(${ITK_USE_FILE}) if(NOT ITK_NO_IO_FACTORY_REGISTER_MANAGER) # We manually add the define which will be of target scope. MITK # patches ITK_USE_FILE to remove the directory scoped compile # definition since it would be propagated to other targets in the # same directory scope but these targets might want to *not* # use the ITK factory manager stuff. target_compile_definitions(${MODULE_TARGET} PRIVATE ITK_IO_FACTORY_REGISTER_MANAGER) endif() endfunction() _itk_create_factory_register_manager() if(BUILD_TESTING) add_subdirectory(TestingHelper) add_subdirectory(test) endif() diff --git a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp index c3df456fad..fb77ebdb61 100644 --- a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp +++ b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp @@ -1,239 +1,240 @@ /*============================================================================ 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. ============================================================================*/ // VTK #include <vtkCamera.h> #include <vtkOpenGLRenderWindow.h> #include <vtkPNGWriter.h> #include <vtkRenderLargeImage.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> // MITK #include <mitkNodePredicateDataType.h> #include <mitkRenderWindow.h> #include <mitkRenderingTestHelper.h> #include <mitkSliceNavigationController.h> #include <mitkStandaloneDataStorage.h> #include <mitkException.h> #include <mitkTestNotRunException.h> #include <mitkTestingMacros.h> +#include <mitkUIDGenerator.h> // VTK Testing to compare the rendered image pixel-wise against a reference screen shot #include "vtkTesting.h" mitk::RenderingTestHelper::RenderingTestHelper(int width, int height, AntiAliasing antiAliasing) : m_AutomaticallyCloseRenderWindow(true) { this->Initialize(width, height, antiAliasing); } mitk::RenderingTestHelper::RenderingTestHelper( int width, int height, int argc, char *argv[], AntiAliasing antiAliasing) : m_AutomaticallyCloseRenderWindow(true) { this->Initialize(width, height, antiAliasing); this->SetInputFileNames(argc, argv); } void mitk::RenderingTestHelper::Initialize(int width, int height, AntiAliasing antiAliasing) { RenderingManager::GetInstance()->SetAntiAliasing(antiAliasing); mitk::UIDGenerator uidGen = mitk::UIDGenerator("UnnamedRenderer_"); m_RenderWindow = mitk::RenderWindow::New(nullptr, uidGen.GetUID().c_str()); auto renderWindow = m_RenderWindow->GetVtkRenderWindow(); if (0 == renderWindow->SupportsOpenGL()) { auto openGLRenderWindow = dynamic_cast<vtkOpenGLRenderWindow*>(renderWindow); auto message = nullptr != openGLRenderWindow ? openGLRenderWindow->GetOpenGLSupportMessage() : std::string("No details available."); mitkThrowException(mitk::TestNotRunException) << "OpenGL not supported: " << message; } m_DataStorage = mitk::StandaloneDataStorage::New(); m_RenderWindow->GetRenderer()->SetDataStorage(m_DataStorage); this->SetMapperIDToRender2D(); this->GetVtkRenderWindow()->SetSize(width, height); m_RenderWindow->GetRenderer()->Resize(width, height); } mitk::RenderingTestHelper::~RenderingTestHelper() { } void mitk::RenderingTestHelper::SetMapperID(mitk::BaseRenderer::StandardMapperSlot id) { m_RenderWindow->GetRenderer()->SetMapperID(id); } void mitk::RenderingTestHelper::SetMapperIDToRender3D() { this->SetMapperID(mitk::BaseRenderer::Standard3D); mitk::RenderingManager::GetInstance()->InitializeViews( this->GetDataStorage()->ComputeBoundingGeometry3D(this->GetDataStorage()->GetAll())); } void mitk::RenderingTestHelper::SetMapperIDToRender2D() { this->SetMapperID(mitk::BaseRenderer::Standard2D); } void mitk::RenderingTestHelper::Render() { // if the datastorage is initialized and at least 1 image is loaded render it if (m_DataStorage.IsNotNull() && m_DataStorage->GetAll()->Size() >= 1) { // Prepare the VTK camera before rendering. m_RenderWindow->GetRenderer()->PrepareRender(); this->GetVtkRenderWindow()->Render(); this->GetVtkRenderWindow()->WaitForCompletion(); if (m_AutomaticallyCloseRenderWindow == false) { // Use interaction to stop the test this->GetVtkRenderWindow()->GetInteractor()->Start(); } } else { MITK_ERROR << "No images loaded in data storage!"; } } mitk::DataStorage::Pointer mitk::RenderingTestHelper::GetDataStorage() { return m_DataStorage; } void mitk::RenderingTestHelper::SetInputFileNames(int argc, char *argv[]) { // i is set 1, because 0 is the testname as string // parse parameters for (int i = 1; i < argc; ++i) { // add everything to a list but -T and -V std::string tmp = argv[i]; if ((tmp.compare("-T")) && (tmp.compare("-V"))) { this->AddToStorage(tmp); } else { break; } } } void mitk::RenderingTestHelper::SetViewDirection(mitk::AnatomicalPlane viewDirection) { mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow()) ->GetSliceNavigationController() ->SetDefaultViewDirection(viewDirection); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } void mitk::RenderingTestHelper::ReorientSlices(mitk::Point3D origin, mitk::Vector3D rotation) { mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow())->GetSliceNavigationController(); sliceNavigationController->ReorientSlices(origin, rotation); } vtkRenderer *mitk::RenderingTestHelper::GetVtkRenderer() { return m_RenderWindow->GetRenderer()->GetVtkRenderer(); } void mitk::RenderingTestHelper::SetImageProperty(const char *propertyKey, mitk::BaseProperty *property) { this->m_DataStorage->GetNode(mitk::NodePredicateDataType::New("Image"))->SetProperty(propertyKey, property); } vtkRenderWindow *mitk::RenderingTestHelper::GetVtkRenderWindow() { return m_RenderWindow->GetVtkRenderWindow(); } bool mitk::RenderingTestHelper::CompareRenderWindowAgainstReference(int argc, char *argv[], double threshold) { this->Render(); // retVal meanings: (see VTK/Rendering/vtkTesting.h) // 0 = test failed // 1 = test passed // 2 = test not run // 3 = something with vtkInteraction if (vtkTesting::Test(argc, argv, this->GetVtkRenderWindow(), threshold) == 1) return true; else return false; } // method to save a screenshot of the renderwindow (e.g. create a reference screenshot) void mitk::RenderingTestHelper::SaveAsPNG(std::string fileName) { vtkSmartPointer<vtkRenderer> renderer = this->GetVtkRenderer(); bool doubleBuffering(renderer->GetRenderWindow()->GetDoubleBuffer()); renderer->GetRenderWindow()->DoubleBufferOff(); vtkSmartPointer<vtkRenderLargeImage> magnifier = vtkSmartPointer<vtkRenderLargeImage>::New(); magnifier->SetInput(renderer); magnifier->SetMagnification(1); vtkSmartPointer<vtkImageWriter> fileWriter = vtkSmartPointer<vtkPNGWriter>::New(); fileWriter->SetInputConnection(magnifier->GetOutputPort()); fileWriter->SetFileName(fileName.c_str()); fileWriter->Write(); renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering); } void mitk::RenderingTestHelper::SetAutomaticallyCloseRenderWindow(bool automaticallyCloseRenderWindow) { m_AutomaticallyCloseRenderWindow = automaticallyCloseRenderWindow; } void mitk::RenderingTestHelper::SaveReferenceScreenShot(std::string fileName) { this->SaveAsPNG(fileName); } void mitk::RenderingTestHelper::AddToStorage(const std::string &filename) { try { mitk::IOUtil::Load(filename, *m_DataStorage.GetPointer()); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } catch ( const itk::ExceptionObject &e ) { MITK_ERROR << "Failed loading test data '" << filename << "': " << e.what(); } } void mitk::RenderingTestHelper::AddNodeToStorage(mitk::DataNode::Pointer node) { this->m_DataStorage->Add(node); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index d8412455d1..1aa29e2075 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,329 +1,328 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkCompositePixelValueToString.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkExtractSliceFilter2.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkTemporalJoinImagesFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkCrosshairManager.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSliceNavigationHelper.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkTimeNavigationController.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkCrosshairData.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkGenericIDRelationRule.cpp DataManagement/mitkIdentifiable.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp + DataManagement/mitkIPropertyDeserialization.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkIPropertyProvider.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp - DataManagement/mitkLine.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 DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDataUID.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSubGeometry.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp + DataManagement/mitkPropertyDeserialization.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyKeyPath.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkPropertyRelationRuleBase.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkSourceImageRelationRule.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp - DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkIPropertyRelations.cpp DataManagement/mitkPropertyRelations.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayActionEventBroadcast.cpp Interactions/mitkDisplayActionEventFunctions.cpp Interactions/mitkDisplayActionEventHandler.cpp Interactions/mitkDisplayActionEventHandlerDesynchronized.cpp Interactions/mitkDisplayActionEventHandlerStd.cpp Interactions/mitkDisplayActionEventHandlerSynchronized.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionSchemeSwitcher.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp - IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLogBackend.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkUtf8Util.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp IO/mitkIOMetaInformationPropertyConstants.cpp IO/mitkIPreferences.cpp IO/mitkPreferences.cpp IO/mitkIPreferencesService.cpp IO/mitkPreferencesService.cpp IO/mitkIPreferencesStorage.cpp IO/mitkXMLPreferencesStorage.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp Rendering/mitkBaseRendererHelper.cpp Rendering/mitkCrosshairVtkMapper2D.cpp Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVideoRecorder.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfigMITKBase.xml Interactions/DisplayConfigPACSBase.xml Interactions/DisplayConfigCrosshair.xml Interactions/DisplayConfigRotation.xml Interactions/DisplayConfigActivateCoupling.xml Interactions/DisplayConfigSwivel.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigBlockLMB.xml Interactions/PointSet.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/mitkAnnotationProperty.h b/Modules/Core/include/mitkAnnotationProperty.h index 310a432405..9c03069090 100644 --- a/Modules/Core/include/mitkAnnotationProperty.h +++ b/Modules/Core/include/mitkAnnotationProperty.h @@ -1,79 +1,83 @@ /*============================================================================ 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 mitkAnnotationProperty_h #define mitkAnnotationProperty_h #include "mitkBaseProperty.h" #include "mitkNumericTypes.h" #include <MitkCoreExports.h> #include <itkConfigure.h> #include <string> namespace mitk { /** * \brief Property for annotations * \ingroup DataManagement */ class MITKCORE_EXPORT AnnotationProperty : public BaseProperty { public: mitkClassMacro(AnnotationProperty, BaseProperty); typedef std::string ValueType; itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro2Param(AnnotationProperty, const char *, const Point3D &); mitkNewMacro2Param(AnnotationProperty, const std::string &, const Point3D &); mitkNewMacro4Param(AnnotationProperty, const char *, ScalarType, ScalarType, ScalarType); mitkNewMacro4Param(AnnotationProperty, const std::string &, ScalarType, ScalarType, ScalarType); itkGetStringMacro(Label); itkSetStringMacro(Label); const Point3D &GetPosition() const; 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=; protected: std::string m_Label; Point3D m_Position; AnnotationProperty(); AnnotationProperty(const char *label, const Point3D &position); AnnotationProperty(const std::string &label, const Point3D &position); AnnotationProperty(const char *label, ScalarType x, ScalarType y, ScalarType z); AnnotationProperty(const std::string &label, ScalarType x, ScalarType y, ScalarType z); AnnotationProperty(const AnnotationProperty &other); private: // purposely not implemented AnnotationProperty &operator=(const AnnotationProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkBaseGeometry.h b/Modules/Core/include/mitkBaseGeometry.h index 4d00557d8d..6dff77d92a 100644 --- a/Modules/Core/include/mitkBaseGeometry.h +++ b/Modules/Core/include/mitkBaseGeometry.h @@ -1,763 +1,764 @@ /*============================================================================ 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 mitkBaseGeometry_h #define mitkBaseGeometry_h #include "mitkOperationActor.h" #include <MitkCoreExports.h> #include <mitkCommon.h> #include "itkScalableAffineTransform.h" #include "mitkNumericTypes.h" #include <itkBoundingBox.h> #include <itkIndex.h> #include <itkQuaternionRigidTransform.h> #include <mitkAffineTransform3D.h> #include <mitkGeometryTransformHolder.h> #include <vtkTransform.h> class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; namespace mitk { //##Documentation //## @brief Standard 3D-BoundingBox typedef //## //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). typedef itk::BoundingBox<unsigned long, 3, ScalarType> BoundingBox; //##Documentation //## @brief Standard typedef for time-bounds typedef itk::FixedArray<ScalarType, 2> TimeBounds; typedef itk::FixedArray<ScalarType, 3> FixedArrayType; //##Documentation //## @brief BaseGeometry Describes the geometry of a data object //## //## The class holds //## \li a bounding box which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels), to be accessed by //## GetBoundingBox() //## \li a transform to convert intrinsic coordinates into a //## world-coordinate system with coordinates in millimeters //## and milliseconds (all are floating point values), to //## be accessed by GetIndexToWorldTransform() //## \li an origin and spacing to define the geometry //## //## BaseGeometry and its sub-classes allow converting between //## intrinsic coordinates (called index or unit coordinates) //## and world-coordinates (called world or mm coordinates), //## e.g. WorldToIndex. //## In case you need integer index coordinates, provide an //## mitk::Index3D (or itk::Index) as target variable to //## WorldToIndex, otherwise you will get a continuous index //## (floating point values). //## //## An important sub-class is SlicedGeometry3D, which describes //## data objects consisting of slices, e.g., objects of type Image. //## Conversions between world coordinates (in mm) and unit coordinates //## (e.g., pixels in the case of an Image) can be performed. //## //## For more information on related classes, see \ref Geometry. //## //## BaseGeometry instances referring to an Image need a slightly //## different definition of corners, see SetImageGeometry. This //## is usually automatically called by Image. //## //## BaseGeometry have to be initialized in the method GenerateOutputInformation() //## of BaseProcess (or CopyInformation/ UpdateOutputInformation of BaseData, //## if possible, e.g., by analyzing pic tags in Image) subclasses. See also //## itk::ProcessObject::GenerateOutputInformation(), //## itk::DataObject::CopyInformation() and //## itk::DataObject::UpdateOutputInformation(). //## //## At least, it can return the bounding box of the data object. //## //## The BaseGeometry class is an abstract class. The most simple implementation //## is the subclass Geometry3D. //## //## Rule: everything is in mm (ms) if not stated otherwise. //## @ingroup Geometry class MITKCORE_EXPORT BaseGeometry : public itk::Object, public OperationActor { public: mitkClassMacroItkParent(BaseGeometry, itk::Object); itkCloneMacro(Self); // ********************************** TypeDef ********************************** typedef GeometryTransformHolder::TransformType TransformType; typedef itk::BoundingBox<unsigned long, 3, ScalarType> BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // ********************************** Origin, Spacing ********************************** //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D GetOrigin() const; //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## void SetOrigin(const Point3D &origin); //##Documentation //## @brief Get the spacing (size of a pixel). //## const mitk::Vector3D GetSpacing() const; //##Documentation //## @brief Set the spacing (m_Spacing). //## //##The spacing is also changed in the IndexToWorldTransform. void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief Get the origin as VnlVector //## //## \sa GetOrigin VnlVector GetOriginVnl() const; // ********************************** other functions ********************************** //##Documentation //## @brief Get the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkGetConstMacro(FrameOfReferenceID, unsigned int); //##Documentation //## @brief Set the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkSetMacro(FrameOfReferenceID, unsigned int); itkGetConstMacro(IndexToWorldTransformLastModified, unsigned long); //##Documentation //## @brief Overload of function Modified() to prohibit several calls of Modified() using the ModifiedLock class. //## //## For the use of Modified(), see class ModifiedLock. void Modified() const override; friend class ModifiedLock; //##Documentation //## @brief Is this BaseGeometry in a state that is valid? //## //## This function returns always true in the BaseGeometry class. Other implementations are possible in subclasses. virtual bool IsValid() const; // ********************************** Initialize ********************************** //##Documentation //## @brief Initialize the BaseGeometry void Initialize(); void InitializeGeometry(Self *newGeometry) const; // ********************************** Transformations Set/Get ********************************** //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates mitk::AffineTransform3D *GetIndexToWorldTransform(); //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates const mitk::AffineTransform3D *GetIndexToWorldTransform() const; //## @brief Set the transformation used to convert from index //## to world coordinates. The spacing of the new transform is //## copied to m_spacing. void SetIndexToWorldTransform(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of //## the new transform is copied to m_spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix); //## @brief Set the transformation used to convert from index //## to world coordinates.This function keeps the original spacing. void SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4. This function keeps the original spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix); //## Get the Vtk Matrix which describes the transform. vtkMatrix4x4 *GetVtkMatrix(); + const vtkMatrix4x4* GetVtkMatrix() const; //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform *GetVtkTransform() const; //##Documentation //## @brief Set the transform to identity, the spacing to 1 and origin to 0 //## void SetIdentity(); // ********************************** Transformations ********************************** //##Documentation //## @brief Compose new IndexToWorldTransform with a given transform. //## //## This method composes m_IndexToWorldTransform with another transform, //## modifying self to be the composition of self and other. //## If the argument pre is true, then other is precomposed with self; //## that is, the resulting transformation consists of first applying //## other to the source, followed by self. If pre is false or omitted, //## then other is post-composed with self; that is the resulting //## transformation consists of first applying self to the source, //## followed by other. //## This method also changes m_spacing. void Compose(const TransformType *other, bool pre = false); //##Documentation //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. //## //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. void Compose(const vtkMatrix4x4 *vtkmatrix, bool pre = false); //##Documentation //## @brief Translate the origin by a vector //## void Translate(const Vector3D &vector); //##Documentation //##@brief executes affine operations (translate, rotate, scale) void ExecuteOperation(Operation *operation) override; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates //## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image), //## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index<VIndexDimension> &index). //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (discrete!) index coordinates. //## This method rounds to integer indices! //## For further information about coordinates types, please see the Geometry documentation template <unsigned int VIndexDimension> void WorldToIndex(const mitk::Point3D &pt_mm, itk::Index<VIndexDimension> &index) const { typedef itk::Index<VIndexDimension> IndexType; mitk::Point3D pt_units; this->WorldToIndex(pt_mm, pt_units); int i, dim = index.GetIndexDimension(); if (dim > 3) { index.Fill(0); dim = 3; } for (i = 0; i < dim; ++i) { index[i] = itk::Math::RoundHalfIntegerUp<typename IndexType::IndexValueType>(pt_units[i]); } } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const; //##Documentation //## @brief Convert (discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation template <unsigned int VIndexDimension> void IndexToWorld(const itk::Index<VIndexDimension> &index, mitk::Point3D &pt_mm) const { mitk::Point3D pt_units; pt_units.Fill(0); int i, dim = index.GetIndexDimension(); if (dim > 3) { dim = 3; } for (i = 0; i < dim; ++i) { pt_units[i] = index[i]; } IndexToWorld(pt_units, pt_mm); } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point3D) is not used. If possible, please use void IndexToWorld(const // mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D &atPt3d_units, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point3D) is not used. If possible, please use void WorldToIndex(const // mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const; //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert ITK physical coordinates of a \em point (in mm, //## but without a rotation) into MITK world coordinates (in mm) //## //## For more information, see WorldToItkPhysicalPoint. template <class TCoordRep> void ItkPhysicalPointToWorld(const itk::Point<TCoordRep, 3> &itkPhysicalPoint, mitk::Point3D &pt_mm) const { mitk::vtk2itk(itkPhysicalPoint, pt_mm); } //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert world coordinates (in mm) of a \em point to //## ITK physical coordinates (in mm, but without a possible rotation) //## //## This method is useful if you have want to access an mitk::Image //## via an itk::Image. ITK v3.8 and older did not support rotated (tilted) //## images, i.e., ITK images are always parallel to the coordinate axes. //## When accessing a (possibly rotated) mitk::Image via an itk::Image //## the rotational part of the transformation in the BaseGeometry is //## simply discarded; in other word: only the origin and spacing is //## used by ITK, not the complete matrix available in MITK. //## With WorldToItkPhysicalPoint you can convert an MITK world //## coordinate (including the rotation) into a coordinate that //## can be used with the ITK image as a ITK physical coordinate //## (excluding the rotation). template <class TCoordRep> void WorldToItkPhysicalPoint(const mitk::Point3D &pt_mm, itk::Point<TCoordRep, 3> &itkPhysicalPoint) const { mitk::vtk2itk(pt_mm, itkPhysicalPoint); } // ********************************** BoundingBox ********************************** /** Get the bounding box */ itkGetConstObjectMacro(BoundingBox, BoundingBoxType); // a bit of a misuse, but we want only doxygen to see the following: #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get bounding box (in index/unit coordinates) itkGetConstObjectMacro(BoundingBox, BoundingBoxType); //##Documentation //## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType const BoundsArrayType GetBounds() const; #endif const BoundsArrayType GetBounds() const; //##Documentation //## \brief Set the bounding box (in index/unit coordinates) //## //## Only possible via the BoundsArray to make clear that a //## copy of the bounding-box is stored, not a reference to it. void SetBounds(const BoundsArrayType &bounds); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a float array void SetFloatBounds(const float bounds[6]); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a double array void SetFloatBounds(const double bounds[6]); //##Documentation //## @brief Get a VnlVector along bounding-box in the specified //## @a direction, length is spacing //## //## \sa GetAxisVector VnlVector GetMatrixColumn(unsigned int direction) const; //##Documentation //## @brief Calculates a bounding-box around the geometry relative //## to a coordinate system defined by a transform //## mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D *transform) const; //##Documentation //## @brief Set the time bounds (in ms) // void SetTimeBounds(const TimeBounds& timebounds); // ********************************** Geometry ********************************** #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the extent of the bounding box (in index/unit coordinates) //## //## To access the extent in mm use GetExtentInMM ScalarType GetExtent(unsigned int direction) const; #endif /** Get the extent of the bounding box */ ScalarType GetExtent(unsigned int direction) const; //##Documentation //## @brief Get the extent of the bounding-box in the specified @a direction in mm //## //## Equals length of GetAxisVector(direction). ScalarType GetExtentInMM(int direction) const; //##Documentation //## @brief Get vector along bounding-box in the specified @a direction in mm //## //## The length of the vector is the size of the bounding-box in the //## specified @a direction in mm //## \sa GetMatrixColumn Vector3D GetAxisVector(unsigned int direction) const; //##Documentation //## @brief Checks, if the given geometry can be converted to 2D without information loss //## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK //## it will be filled with standard values. This function checks, if information would be lost during this //## procedure virtual bool Is2DConvertable(); //##Documentation //## @brief Get the center of the bounding-box in mm //## Point3D GetCenter() const; //##Documentation //## @brief Get the squared length of the diagonal of the bounding-box in mm //## double GetDiagonalLength2() const; //##Documentation //## @brief Get the length of the diagonal of the bounding-box in mm //## double GetDiagonalLength() const; //##Documentation //## @brief Get the position of the corner number \a id (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(int id) const; //##Documentation //## @brief Get the position of a corner (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(bool xFront = true, bool yFront = true, bool zFront = true) const; //##Documentation //## @brief Set the extent of the bounding-box in the specified @a direction in mm //## //## @note This changes the matrix in the transform, @a not the bounds, which are given in units! void SetExtentInMM(int direction, ScalarType extentInMM); //##Documentation //## @brief Test whether the point \a p (world coordinates in mm) is //## inside the bounding box bool IsInside(const mitk::Point3D &p) const; //##Documentation //## @brief Test whether the point \a p ((continuous!)index coordinates in units) is //## inside the bounding box bool IsIndexInside(const mitk::Point3D &index) const; //##Documentation //## @brief Convenience method for working with ITK indices template <unsigned int VIndexDimension> bool IsIndexInside(const itk::Index<VIndexDimension> &index) const { int i, dim = index.GetIndexDimension(); Point3D pt_index; pt_index.Fill(0); for (i = 0; i < dim; ++i) { pt_index[i] = index[i]; } return IsIndexInside(pt_index); } // ********************************* Image Geometry ******************************** //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to //change // the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and // changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry); //##Documentation //## @brief Is this an ImageGeometry? //## //## For more information, see SetImageGeometry itkGetConstMacro(ImageGeometry, bool) //##Documentation //## @brief Define that this BaseGeometry is referring to an Image //## //## A geometry referring to an Image needs a slightly different //## definition of the position of the corners (see GetCornerPoint). //## The position of a voxel is defined by the position of its center. //## If we would use the origin (position of the (center of) the first //## voxel) as a corner and display this point, it would seem to be //## \em not at the corner but a bit within the image. Even worse for //## the opposite corner of the image: here the corner would appear //## outside the image (by half of the voxel diameter). Thus, we have //## to correct for this and to be able to do that, we need to know //## that the BaseGeometry is referring to an Image. itkSetMacro(ImageGeometry, bool); itkBooleanMacro(ImageGeometry); const GeometryTransformHolder *GetGeometryTransformHolder() const; //##Documentation //## @brief One to one mapping of axes to world orientations. //## //## The result is stored in the output argument that must be an array of three int values. //## The elements of the array will be the axis indices that correspond to the sagittal, //## coronal and axial orientations, in this order. It is guaranteed that each axis will //## be mapped to different orientations. //## //## @param axes Output argument that will store the axis indices for each orientation. void MapAxesToOrientations(int axes[]) const; protected: // ********************************** Constructor ********************************** BaseGeometry(); BaseGeometry(const BaseGeometry &other); ~BaseGeometry() override; itk::LightObject::Pointer InternalClone() const override = 0; void PrintSelf(std::ostream &os, itk::Indent indent) const override; static const std::string GetTransformAsString(TransformType *transformType); itkGetConstMacro(NDimensions, unsigned int); bool IsBoundingBoxNull() const; bool IsIndexToWorldTransformNull() const; void SetVtkMatrixDeepCopy(vtkTransform *vtktransform); void _SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief PreSetSpacing //## //## These virtual function allows a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use //## {Superclass::PreSetSpacing();}; virtual void PreSetSpacing(const mitk::Vector3D & /*aSpacing*/){}; //##Documentation //## @brief CheckBounds //## //## This function is called in SetBounds. Assertions can be implemented in this function (see PlaneGeometry.cpp). //## If you implement this function in a subclass, make sure, that all classes were your class inherits from //## have an implementation of CheckBounds //## (e.g. inheritance BaseGeometry <- A <- B. Implementation of CheckBounds in class B needs implementation in A as // well!) virtual void CheckBounds(const BoundsArrayType & /*bounds*/){}; //##Documentation //## @brief CheckIndexToWorldTransform //## //## This function is called in SetIndexToWorldTransform. Assertions can be implemented in this function (see // PlaneGeometry.cpp). //## In Subclasses of BaseGeometry, implement own conditions or call Superclass::CheckBounds(bounds);. virtual void CheckIndexToWorldTransform(mitk::AffineTransform3D * /*transform*/){}; private: GeometryTransformHolder *m_GeometryTransform; void InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry); //##Documentation //## @brief Bounding Box, which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels) BoundingBoxPointer m_BoundingBox; unsigned int m_FrameOfReferenceID; // mitk::TimeBounds m_TimeBounds; static const unsigned int m_NDimensions = 3; mutable TransformType::Pointer m_InvertedTransform; mutable unsigned long m_IndexToWorldTransformLastModified; bool m_ImageGeometry; //##Documentation //## @brief ModifiedLockFlag is used to prohibit the call of Modified() //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the ModifiedLock class! bool m_ModifiedLockFlag; //##Documentation //## @brief ModifiedcalledFlag is used to collect calls of Modified(). //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the Modified() function! mutable bool m_ModifiedCalledFlag; }; // ********************************** Equal Functions ********************************** // // Static compare functions mainly for testing // /** * @brief Equal A function comparing two geometries for being identical. * * @ingroup MITKTestingAPI * * The function compares the spacing, origin, axisvectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolarence value for all methods which are internally used for comparison. * If you want to use different tolerance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment). * You can use mitk::eps in most cases. * @param directionEps Tolerance for comparison of all directional aspects (axis). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType coordinateEps, ScalarType directionEps, bool verbose = false); /** * @brief Equal A function comparing two geometries for being identical. * * @ingroup MITKTestingAPI * * This is an overloaded version that uses a single tolerance for spatial and directional aspects. For more details, * see the other overloaded version. * * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType eps = mitk::eps, bool verbose = false); /** * @brief Equal A function comparing two transforms (TransformType) for being identical. * * @ingroup MITKTestingAPI * * The function compares the IndexToWorldTransform (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparison. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose); /** * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for being identical. * * @ingroup MITKTestingAPI * * The function compares the bounds (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparison. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose); /** * @brief A function checks if a test geometry is a sub geometry of * a given reference geometry. * * Sub geometry means that both geometries have the same voxel grid (same spacing, same axes, * origin is on voxel grid), but the bounding box of the checked geometry is contained or equal * to the bounding box of the reference geometry.\n * By this definition equal geometries are always sub geometries of each other. * * The function checks the spacing, origin, axis vectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolerance value for all methods which are internally used for comparison. * @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo. * @param referenceGeo Geometry that should contain testedGeometry as sub geometry. * @param coordinateEps Tolerance for comparison of all spatial aspects (spacing, origin and grid alignment). * You can use mitk::eps in most cases. * @param directionEps Tolerance for comparison of all directional aspects (axis). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparisons are true. False otherwise. */ MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType coordinateEps, ScalarType directionEps, bool verbose = false); /** * @brief A function checks if a test geometry is a sub geometry of * a given reference geometry. * * This is a overloaded version that uses a single tolerance for spatial and directional aspects. For more details, * see the other overloaded version. * * @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo. * @param referenceGeo Geometry that should contain testedGeometry as sub geometry. * @param eps Tolarence for comparison (both spatial and directional). You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False otherwise. */ MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType eps = mitk::eps, bool verbose = false); } // namespace mitk #endif diff --git a/Modules/Core/include/mitkBaseProperty.h b/Modules/Core/include/mitkBaseProperty.h index 05a305dc50..f99c775f5d 100644 --- a/Modules/Core/include/mitkBaseProperty.h +++ b/Modules/Core/include/mitkBaseProperty.h @@ -1,98 +1,115 @@ /*============================================================================ 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 mitkBaseProperty_h #define mitkBaseProperty_h #include <MitkCoreExports.h> #include <itkObjectFactory.h> #include <mitkCommon.h> #include <string> +#include <nlohmann/json_fwd.hpp> namespace mitk { /*! \brief Abstract base class for properties \ingroup DataManagement Base class for properties. Properties are arbitrary additional information (to define a new type of information you have to define a subclass of BaseProperty) that can be added to a PropertyList. Concrete subclasses of BaseProperty should define Set-/Get-methods to assess the property value, which should be stored by value (not by reference). Subclasses must implement an operator==(const BaseProperty& property), which is used by PropertyList to check whether a property has been changed. */ class MITKCORE_EXPORT BaseProperty : public itk::Object { public: mitkClassMacroItkParent(BaseProperty, itk::Object); itkCloneMacro(Self); /*! @brief Subclasses must implement IsEqual(const BaseProperty&) to support comparison. operator== which is used by PropertyList to check whether a property has been changed. */ bool operator==(const BaseProperty &property) const; /*! @brief Assigns property to this BaseProperty instance. Subclasses must implement Assign(const BaseProperty&) and call the superclass Assign method for proper handling of polymorphic assignments. The assignment operator of the subclass should be disabled and the baseclass operator should be made visible using "using" statements. */ BaseProperty &operator=(const BaseProperty &property); /*! @brief Assigns property to this BaseProperty instance. This method is identical to the assignment operator, except for the return type. It allows to directly check if the assignment was successful. */ bool AssignProperty(const BaseProperty &property); virtual std::string GetValueAsString() const; + /** \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 */ static const std::string VALUE_CANNOT_BE_CONVERTED_TO_STRING; protected: BaseProperty(); BaseProperty(const BaseProperty &other); ~BaseProperty() override; private: /*! Override this method in subclasses to implement a meaningful comparison. The property argument is guaranteed to be castable to the type of the implementing subclass. */ virtual bool IsEqual(const BaseProperty &property) const = 0; /*! Override this method in subclasses to implement a meaningful assignment. The property argument is guaranteed to be castable to the type of the implementing subclass. @warning This is not yet exception aware/safe and if this method returns false, this property's state might be undefined. @return True if the argument could be assigned to this property. */ virtual bool Assign(const BaseProperty &) = 0; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkClippingProperty.h b/Modules/Core/include/mitkClippingProperty.h index 87bdc056b1..9e3f532572 100644 --- a/Modules/Core/include/mitkClippingProperty.h +++ b/Modules/Core/include/mitkClippingProperty.h @@ -1,87 +1,90 @@ /*============================================================================ 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 mitkClippingProperty_h #define mitkClippingProperty_h #include "mitkBaseProperty.h" #include "mitkNumericTypes.h" #include <MitkCoreExports.h> #include <itkConfigure.h> #include <string> namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * \brief Property for clipping datasets; currently only * clipping planes are possible * \ingroup DataManagement */ class MITKCORE_EXPORT ClippingProperty : public BaseProperty { public: mitkClassMacro(ClippingProperty, BaseProperty); typedef std::string ValueType; itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro2Param(ClippingProperty, const Point3D &, const Vector3D &); bool GetClippingEnabled() const; void SetClippingEnabled(bool enabled); const Point3D &GetOrigin() const; void SetOrigin(const Point3D &origin); const Vector3D &GetNormal() const; void SetNormal(const Vector3D &normal); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: bool m_ClippingEnabled; Point3D m_Origin; Vector3D m_Normal; ClippingProperty(); ClippingProperty(const ClippingProperty &other); ClippingProperty(const Point3D &origin, const Vector3D &normal); private: // purposely not implemented ClippingProperty &operator=(const ClippingProperty &); bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; itk::LightObject::Pointer InternalClone() const override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkColorProperty.h b/Modules/Core/include/mitkColorProperty.h index 0b1809c0fb..8b31242324 100644 --- a/Modules/Core/include/mitkColorProperty.h +++ b/Modules/Core/include/mitkColorProperty.h @@ -1,96 +1,121 @@ /*============================================================================ 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 mitkColorProperty_h #define mitkColorProperty_h -#include "mitkBaseProperty.h" +#include <mitkBaseProperty.h> #include <MitkCoreExports.h> + #include <itkRGBPixel.h> +#include <nlohmann/json.hpp> + namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief Color Standard RGB color typedef (float) * * Standard RGB color typedef to get rid of template argument (float). * Color range is from 0.0f to 1.0f for each component. * * @ingroup Property */ typedef itk::RGBPixel<float> Color; /** * @brief The ColorProperty class RGB color property * @ingroup DataManagement * * @note If you want to apply the mitk::ColorProperty to an mitk::Image * make sure to set the mitk::RenderingModeProperty to a mode which * supports color (e.g. LEVELWINDOW_COLOR). For an example how to use * the mitk::ColorProperty see mitkImageVtkMapper2DColorTest.cpp in * Core/Code/Rendering. */ class MITKCORE_EXPORT ColorProperty : public BaseProperty { protected: mitk::Color m_Color; ColorProperty(); ColorProperty(const ColorProperty &other); ColorProperty(const float red, const float green, const float blue); ColorProperty(const float color[3]); ColorProperty(const mitk::Color &color); public: mitkClassMacro(ColorProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(ColorProperty, const float *); mitkNewMacro1Param(ColorProperty, const mitk::Color &); mitkNewMacro3Param(ColorProperty, const float, const float, const float); typedef mitk::Color ValueType; const mitk::Color &GetColor() const; const mitk::Color &GetValue() const; std::string GetValueAsString() const override; void SetColor(const mitk::Color &color); void SetValue(const mitk::Color &color); void SetColor(float red, float green, float blue); + bool ToJSON(nlohmann::json &j) const override; + bool FromJSON(const nlohmann::json &j) override; + using BaseProperty::operator=; private: // purposely not implemented ColorProperty &operator=(const ColorProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk +namespace itk +{ + template <typename TComponent> + void to_json(nlohmann::json& j, const RGBPixel<TComponent>& c) + { + j = nlohmann::json::array(); + + for (size_t i = 0; i < 3; ++i) + j.push_back(c[i]); + } + + template <typename TComponent> + void from_json(const nlohmann::json& j, RGBPixel<TComponent>& 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/mitkCoreServices.h b/Modules/Core/include/mitkCoreServices.h index ce30201441..f02e895e09 100644 --- a/Modules/Core/include/mitkCoreServices.h +++ b/Modules/Core/include/mitkCoreServices.h @@ -1,187 +1,195 @@ /*============================================================================ 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 mitkCoreServices_h #define mitkCoreServices_h #include "MitkCoreExports.h" #include <mitkCommon.h> #include <mitkLog.h> #include <mitkServiceInterface.h> #include <usGetModuleContext.h> #include <usModuleContext.h> #include <usServiceReference.h> #include <cassert> namespace mitk { struct IMimeTypeProvider; class IPropertyAliases; class IPropertyDescriptions; + class IPropertyDeserialization; class IPropertyExtensions; class IPropertyFilters; class IPropertyPersistence; class IPropertyRelations; class IPreferencesService; /** * @brief Access MITK core services. * * This class can be used to conveniently access common * MITK Core service objects. Some getter methods where implementations * exist in the core library are guaranteed to return a non-nullptr service object. * * To ensure that CoreServices::Unget() is called after the caller * has finished using a service object, you should use the CoreServicePointer * helper class which calls Unget() when it goes out of scope: * * \code * CoreServicePointer<IShaderRepository> shaderRepo(CoreServices::GetShaderRepository()); * // Do something with shaderRepo * \endcode * * @see CoreServicePointer */ class MITKCORE_EXPORT CoreServices { public: /** * @brief Get an IPropertyAliases instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyAliases instance. */ static IPropertyAliases *GetPropertyAliases(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPropertyDescriptions instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyDescriptions instance. */ 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. * @return A non-nullptr IPropertyExtensions instance. */ static IPropertyExtensions *GetPropertyExtensions(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPropertyFilters instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyFilters instance. */ static IPropertyFilters *GetPropertyFilters(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPropertyPersistence instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyPersistence instance. */ static IPropertyPersistence *GetPropertyPersistence(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPropertyRelations instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPropertyRelations instance. */ static IPropertyRelations *GetPropertyRelations(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IMimeTypeProvider instance. * @param context The module context of the module getting the service. * @return A non-nullptr IMimeTypeProvider instance. */ static IMimeTypeProvider *GetMimeTypeProvider(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Get an IPreferencesService instance. * @param context The module context of the module getting the service. * @return A non-nullptr IPreferencesService instance. * @sa IPreferences */ static IPreferencesService *GetPreferencesService(us::ModuleContext *context = us::GetModuleContext()); /** * @brief Unget a previously acquired service instance. * @param service The service instance to be released. * @param context * @return \c true if ungetting the service was successful, \c false otherwise. */ template <class S> static bool Unget(S *service, us::ModuleContext *context = us::GetModuleContext()) { return Unget(context, us_service_interface_iid<S>(), service); } private: static bool Unget(us::ModuleContext *context, const std::string &interfaceId, void *service); // purposely not implemented CoreServices(); CoreServices(const CoreServices &); CoreServices &operator=(const CoreServices &); }; /** * @brief A RAII helper class for core service objects. * * This is class is intended for usage in local scopes; it calls * CoreServices::Unget(S*) in its destructor. You should not construct * multiple CoreServicePointer instances using the same service pointer, * unless it is retrieved by a new call to a CoreServices getter method. * * @see CoreServices */ template <class S> class MITK_LOCAL CoreServicePointer { public: explicit CoreServicePointer(S *service, us::ModuleContext* context = us::GetModuleContext()) : m_Service(service), m_Context(context) { assert(service); } ~CoreServicePointer() { try { CoreServices::Unget(m_Service, m_Context); } catch (const std::exception &e) { MITK_ERROR << e.what(); } catch (...) { MITK_ERROR << "Ungetting core service failed."; } } S *operator->() const { return m_Service; } private: S *const m_Service; us::ModuleContext* m_Context; }; } #endif diff --git a/Modules/Core/include/mitkEnumerationProperty.h b/Modules/Core/include/mitkEnumerationProperty.h index 251f1cce82..7771762a5b 100644 --- a/Modules/Core/include/mitkEnumerationProperty.h +++ b/Modules/Core/include/mitkEnumerationProperty.h @@ -1,198 +1,208 @@ /*============================================================================ 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 mitkEnumerationProperty_h #define mitkEnumerationProperty_h #include <mitkBaseProperty.h> #include <map> #include <string> #ifdef _MSC_VER # pragma warning(push) # pragma warning(disable: 4522) // "multiple assignment operators specified" #endif namespace mitk { /** * This class may be used to store properties similar to enumeration values. * Each enumeration value is identified by an id and a name. Note that both * name and id must be unique. Add enumeration values before you use the * Get/SetValue methods. * * To use this class, create a subclass that adds the possible enumeration * values in its constructor. You should override AddEnum() as protected so * that the user isn't able to add invalid enumeration values. * * As example see mitk::VtkRepresentationProperty or * mitk::VtkInterpolationProperty. * * @ingroup DataManagement */ class MITKCORE_EXPORT EnumerationProperty : public BaseProperty { public: mitkClassMacro(EnumerationProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Represents the unique id which is assigned to each enumeration name. */ typedef unsigned int IdType; /** * Type used to store a mapping from enumeration id to enumeration name. */ typedef std::map<IdType, std::string> EnumIdsContainerType; /** * Type used to store a mapping from enumeration name to enumeration id. */ typedef std::map<std::string, IdType> EnumStringsContainerType; /** * Type used for iterators over all defined enumeration values. */ typedef EnumIdsContainerType::const_iterator EnumConstIterator; /** * Adds an enumeration value into the enumeration. The name and id provided * must be unique. This is checked while adding the new enumeration value. * If it is not unique, false is returned. If addition was successful, true * is returned. * @param name The unique name of the enumeration value * @param id The unique id of the enumeration value * @returns True, if the name/id combination was successfully added to the * enumeration values. Otherwise false. */ virtual bool AddEnum(const std::string &name, const IdType &id); /** * Sets the current value of the enumeration. * @param name The name of the enumeration value to set * @returns True if the value was successfully set. Otherwise false. */ virtual bool SetValue(const std::string &name); /** * Sets the current value of the enumeration. * @param id The id of the enumeration value to set * @returns True, if the value was successfully set. Otherwise false. */ virtual bool SetValue(const IdType &id); /** * Returns the id of the current enumeration value. If it was not set so far, * the return value is unspecified. */ virtual IdType GetValueAsId() const; /** * Returns the name of the current enumeration value. If it was not set so far, * the return value is unspecified. */ std::string GetValueAsString() const override; /** * Clears all enumerations including the current one. */ virtual void Clear(); /** * Determines the number of enumeration values. */ virtual EnumIdsContainerType::size_type Size() const; /** * Provides access to the set of enumeration values. The name can be * accessed with iterator->second, the id via iterator->first. * @returns An iterator over all enumeration values. */ virtual EnumConstIterator Begin() const; /** * Specifies the end of the range of enumeration values. * @returns An iterator pointing past the last enumeration values. */ virtual EnumConstIterator End() const; /** * Returns the name for the given id. * @param id The id for which the name should be determined. * If the id is invalid, the return value is unspecified. * @returns The name of the determined enumeration value. */ virtual std::string GetEnumString(const IdType &id) const; /** * Returns the id for the given name. * @param name The enumeration name for which the id should be determined. * If the name is invalid, the return value is unspecified. * @returns The id of the determined enumeration value. */ virtual IdType GetEnumId(const std::string &name) const; /** * Determines if a given id is valid. * @param id The id to check * @returns True if the given id is valid. Otherwise false. */ virtual bool IsValidEnumerationValue(const IdType &id) const; /** * Determines if a given name is valid. * @param name The name to check * @returns True if the given name is valid. Otherwise false. */ virtual bool IsValidEnumerationValue(const std::string &name) const; const EnumIdsContainerType &GetEnumIds() const; const EnumStringsContainerType &GetEnumStrings() const; 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=; EnumerationProperty & operator=(const EnumerationProperty &) = delete; protected: /** * Default constructor. The current value of the enumeration is undefined. */ EnumerationProperty(); EnumerationProperty(const EnumerationProperty &); bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; mitkCloneMacro(Self); private: IdType m_CurrentValue; - - typedef std::map<std::string, EnumIdsContainerType> IdMapForClassNameContainerType; - typedef std::map<std::string, EnumStringsContainerType> StringMapForClassNameContainerType; - EnumIdsContainerType m_IdMap; EnumStringsContainerType m_NameMap; }; } #ifdef _MSC_VER # pragma warning(pop) #endif #endif diff --git a/Modules/Core/include/mitkGenericLookupTable.h b/Modules/Core/include/mitkGenericLookupTable.h index c0ad940072..a450e3b125 100644 --- a/Modules/Core/include/mitkGenericLookupTable.h +++ b/Modules/Core/include/mitkGenericLookupTable.h @@ -1,138 +1,158 @@ /*============================================================================ 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 mitkGenericLookupTable_h #define mitkGenericLookupTable_h #include <map> #include <sstream> #include <cstdlib> #include <string> #include <itkDataObject.h> #include "mitkNumericTypes.h" #include <MitkCoreExports.h> +#include <nlohmann/json.hpp> + namespace mitk { + template <typename T> class GenericLookupTable; + template <typename T> void from_json(const nlohmann::json&, GenericLookupTable<T>&); + /** * @brief Template class for generating lookup-tables * * This class template can be instantiated for all classes/internal types that fulfills * these requirements: * - 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 * * The main purpose of this class is to be used in conjunction with * GenericLookupTableProperty. This enables passing of arbitrary lookup * tables to mappers to configure the rendering process. */ template <typename T> class GenericLookupTable { public: typedef unsigned int IdentifierType; typedef T ValueType; typedef std::map<IdentifierType, ValueType> LookupTableType; typedef GenericLookupTable Self; GenericLookupTable() {} virtual ~GenericLookupTable() {} virtual const char *GetNameOfClass() const { return "GenericLookupTable"; } void SetTableValue(IdentifierType id, ValueType value) { m_LookupTable[id] = value; } bool ValueExists(IdentifierType id) const { auto it = m_LookupTable.find(id); return (it != m_LookupTable.end()); } ValueType GetTableValue(IdentifierType id) const { auto it = m_LookupTable.find(id); if (it != m_LookupTable.end()) return it->second; else throw std::range_error("id does not exist in the lookup table"); } const LookupTableType &GetLookupTable() const { return m_LookupTable; } bool operator==(const Self &lookupTable) const { return (m_LookupTable == lookupTable.m_LookupTable); } bool operator!=(const Self &lookupTable) const { return !(m_LookupTable == lookupTable.m_LookupTable); } virtual Self &operator=(const Self &other) // \TODO: this needs to be unit tested! { if (this == &other) { return *this; } else { m_LookupTable.clear(); m_LookupTable = other.m_LookupTable; return *this; } } + friend void from_json<>(const nlohmann::json&, GenericLookupTable<T>&); + protected: LookupTableType m_LookupTable; }; + + template <typename T> + void to_json(nlohmann::json& j, const GenericLookupTable<T>& t) + { + j = t.GetLookupTable(); + } + + template <typename T> + void from_json(const nlohmann::json& j, GenericLookupTable<T>& t) + { + j.get_to(t.m_LookupTable); + } + } // namespace mitk /** * Generates a specialized subclass of mitk::GenericLookupTable. * This way, GetNameOfClass() returns the value provided by LookupTableName. * Please see mitkProperties.h for examples. * @param LookupTableName the name of the instantiation of GenericLookupTable * @param Type the value type of the GenericLookupTable */ #define mitkSpecializeGenericLookupTable(LookupTableName, Type) \ \ class MITKCORE_EXPORT LookupTableName : public GenericLookupTable<Type> \ \ { \ public: \ typedef LookupTableName Self; \ typedef GenericLookupTable<Type> Superclass; \ virtual const char *GetNameOfClass() const { return #LookupTableName; } \ LookupTableName() {} \ virtual Superclass &operator=(const Superclass &other) { return Superclass::operator=(other); } \ virtual ~LookupTableName() {} \ }; \ \ MITKCORE_EXPORT std::ostream &operator<<(std::ostream &stream, const LookupTableName & /*l*/); /** * Generates the ostream << operator for the lookuptable. This definition * of a global function must be in a cpp file, therefore it is split from the * class declaration macro mitkSpecializeGenericLookupTable. */ #define mitkSpecializeGenericLookupTableOperator(LookupTableName) \ \ std::ostream &mitk::operator<<(std::ostream &stream, const LookupTableName &l) \ \ { \ typedef LookupTableName::LookupTableType::const_iterator IterType; \ IterType e = l.GetLookupTable().end(); \ IterType b = l.GetLookupTable().begin(); \ stream << "["; \ for (IterType i = b; i != e; ++i) \ { \ if (i != b) \ { \ stream << ", "; \ } \ stream << i->first << " -> " << i->second; \ } \ return stream << "]"; \ }; #endif diff --git a/Modules/Core/include/mitkGenericProperty.h b/Modules/Core/include/mitkGenericProperty.h index ceff2ad5e2..b95907e77b 100644 --- a/Modules/Core/include/mitkGenericProperty.h +++ b/Modules/Core/include/mitkGenericProperty.h @@ -1,142 +1,167 @@ /*============================================================================ 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 mitkGenericProperty_h #define mitkGenericProperty_h #include <sstream> #include <cstdlib> #include <string> #include "mitkBaseProperty.h" #include "mitkNumericTypes.h" #include <MitkCoreExports.h> namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /*! @ brief Template class for generating properties for int, float, bool, etc. This class template can be instantiated for all classes/internal types that fulfills these requirements: - 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 <typename T> class MITK_EXPORT GenericProperty : public BaseProperty { public: mitkClassMacro(GenericProperty, BaseProperty); mitkNewMacro1Param(GenericProperty<T>, T); itkCloneMacro(Self); typedef T ValueType; itkSetMacro(Value, T); itkGetConstMacro(Value, T); std::string GetValueAsString() const override { std::stringstream myStr; myStr << GetValue(); return myStr.str(); } + bool ToJSON(nlohmann::json&) const override + { + return false; + } + + bool FromJSON(const nlohmann::json&) override + { + return false; + } + using BaseProperty::operator=; protected: GenericProperty() {} GenericProperty(T x) : m_Value(x) {} GenericProperty(const GenericProperty &other) : BaseProperty(other), m_Value(other.m_Value) {} T m_Value; private: // purposely not implemented GenericProperty &operator=(const GenericProperty &); itk::LightObject::Pointer InternalClone() const override { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } bool IsEqual(const BaseProperty &other) const override { return (this->m_Value == static_cast<const Self &>(other).m_Value); } bool Assign(const BaseProperty &other) override { this->m_Value = static_cast<const Self &>(other).m_Value; return true; } }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk /** * Generates a specialized subclass of mitk::GenericProperty. * This way, GetNameOfClass() returns the value provided by PropertyName. * Please see mitkProperties.h for examples. * @param PropertyName the name of the subclass of GenericProperty * @param Type the value type of the GenericProperty * @param Export the export macro for DLL usage */ #define mitkDeclareGenericProperty(PropertyName, Type, Export) \ \ class Export PropertyName : public GenericProperty<Type> \ \ { \ public: \ mitkClassMacro(PropertyName, GenericProperty<Type>); \ itkFactorylessNewMacro(Self); \ itkCloneMacro(Self); \ mitkNewMacro1Param(PropertyName, Type); \ \ + bool ToJSON(nlohmann::json& j) const override; \ + bool FromJSON(const nlohmann::json& j) override; \ + \ using BaseProperty::operator=; \ \ protected: \ PropertyName(); \ PropertyName(const PropertyName &); \ PropertyName(Type x); \ \ private: \ itk::LightObject::Pointer InternalClone() const override; \ }; #define mitkDefineGenericProperty(PropertyName, Type, DefaultValue) \ mitk::PropertyName::PropertyName() : Superclass(DefaultValue) {} \ mitk::PropertyName::PropertyName(const PropertyName &other) : GenericProperty<Type>(other) {} \ mitk::PropertyName::PropertyName(Type x) : Superclass(x) {} \ itk::LightObject::Pointer mitk::PropertyName::InternalClone() const \ { \ 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<Type>()); \ + return true; \ } #endif diff --git a/Modules/Core/include/mitkGroupTagProperty.h b/Modules/Core/include/mitkGroupTagProperty.h index e83ff9b0e5..9d17596bc1 100644 --- a/Modules/Core/include/mitkGroupTagProperty.h +++ b/Modules/Core/include/mitkGroupTagProperty.h @@ -1,62 +1,65 @@ /*============================================================================ 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 mitkGroupTagProperty_h #define mitkGroupTagProperty_h #include <mitkBaseProperty.h> namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /*! @brief Property class that has no value. @ingroup DataManagement The GroupTag property is used to tag a datatree node to show, that it is member of a group of datatree nodes. This can be used to build groups of datatreenodes without the need to contain them in a specific hiearchic order in the datatree */ class MITKCORE_EXPORT GroupTagProperty : public BaseProperty { public: mitkClassMacro(GroupTagProperty, BaseProperty); 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(); GroupTagProperty(const GroupTagProperty &); private: // purposely not implemented GroupTagProperty &operator=(const GroupTagProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkIPropertyDeserialization.h b/Modules/Core/include/mitkIPropertyDeserialization.h new file mode 100644 index 0000000000..7911bceb00 --- /dev/null +++ b/Modules/Core/include/mitkIPropertyDeserialization.h @@ -0,0 +1,61 @@ +/*============================================================================ + +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 <mitkBaseProperty.h> +#include <mitkServiceInterface.h> +#include <MitkCoreExports.h> + +#include <itkSmartPointer.h> + +#include <string> +#include <type_traits> + +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: + virtual ~IPropertyDeserialization(); + + 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 <typename T, typename = std::enable_if_t<std::is_base_of_v<BaseProperty, T>>> + void RegisterProperty() + { + this->InternalRegisterProperty(T::New()); + } + + protected: + virtual void InternalRegisterProperty(const BaseProperty* property) = 0; + }; +} + +MITK_DECLARE_SERVICE_INTERFACE(mitk::IPropertyDeserialization, "org.mitk.IPropertyDeserialization") + +#endif diff --git a/Modules/Core/include/mitkLevelWindow.h b/Modules/Core/include/mitkLevelWindow.h index f81f33c200..3b0c2006ad 100644 --- a/Modules/Core/include/mitkLevelWindow.h +++ b/Modules/Core/include/mitkLevelWindow.h @@ -1,263 +1,268 @@ /*============================================================================ 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 mitkLevelWindow_h #define mitkLevelWindow_h #include "mitkNumericTypes.h" #include <MitkCoreExports.h> +#include <nlohmann/json_fwd.hpp> namespace mitk { class Image; /** * @brief The LevelWindow class Class to store level/window values. * * Current min and max value are stored in m_LowerWindowBound and m_UpperWindowBound. * m_DefaultLevel amd m_DefaultWindow store the initial Level/Window values for the image. * m_DefaultRangeMin and m_DefaultRangeMax store the initial minrange and maxrange for the image. * * The finite maximum and minimum of valid value range is stored in m_RangeMin and m_RangeMax. * If deduced from an image by default the minimum or maximum of it statistics is used. If one * of these values are infinite the 2nd extrimum (which is guaranteed to be finite), will be used. * * See documentation of SetAuto for information on how the level window is initialized from an image. * * @ingroup DataManagement * * @note If you want to apply the mitk::LevelWindow to an mitk::Image, make sure * to use the mitk::LevelWindowProperty and set the mitk::RenderingModeProperty * to a mode which supports level window (e.g. LEVELWINDOW_COLOR). * Make sure to check the documentation of the mitk::RenderingModeProperty. For a * code example how to use the mitk::LevelWindowProperty check the * mitkImageVtkMapper2DLevelWindowTest.cpp in Core/Code/Testing. */ class MITKCORE_EXPORT LevelWindow { public: LevelWindow(ScalarType level = 127.5, ScalarType window = 255.0); LevelWindow(const mitk::LevelWindow &levWin); virtual ~LevelWindow(); /*! * \brief method that returns the level value, i.e. the center of * the current grey value interval */ ScalarType GetLevel() const; /*! * \brief returns the current window size, i.e the range size of the current grey value interval */ ScalarType GetWindow() const; /*! * \brief method returns the default level value for the image */ ScalarType GetDefaultLevel() const; /*! * \brief returns the default window size for the image */ ScalarType GetDefaultWindow() const; /*! * \brief Resets the level and the window value to the default values */ void ResetDefaultLevelWindow(); /*! * Returns the minimum Value of the window */ ScalarType GetLowerWindowBound() const; /*! * Returns the upper window bound value of the window */ ScalarType GetUpperWindowBound() const; /*! * To set the level and the window value */ void SetLevelWindow(ScalarType level, ScalarType window, bool expandRangesIfNecessary = true); /*! * Set the lower and upper bound of the window, restricted to the range from -10^300 to 10^300. Higher/lower values are clamped to these boundaries. */ void SetWindowBounds(ScalarType lowerBound, ScalarType upperBound, bool expandRangesIfNecessary = true); /*! * sets the window to its maximum Size in scaleRange */ void SetToMaxWindowSize(); /*! * Set the range minimum and maximum value */ void SetRangeMinMax(ScalarType min, ScalarType max); /*! * Get the range minimum value */ ScalarType GetRangeMin() const; /*! * Get the range maximum value */ ScalarType GetRangeMax() const; /*! * Get the default range minimum value */ ScalarType GetDefaultLowerBound() const; /*! * Get the default range maximum value */ ScalarType GetDefaultUpperBound() const; /*! * \brief the default min and max range for image will be reset */ void ResetDefaultRangeMinMax(); /**! * \brief returns the size of the grey value range */ ScalarType GetRange() const; /*! * set the default level and window value */ void SetDefaultLevelWindow(ScalarType level, ScalarType window); /*! * set the default Boundaries */ void SetDefaultBoundaries(ScalarType low, ScalarType up); /**! * \brief sets level/window to optimize the contrast of the given Image */ void SetAuto(const Image *image, bool tryPicTags = true, bool guessByCentralSlice = true, unsigned selectedComponent = 0); /**! * \brief sets level/window to the min/max greyvalues of the given Image */ void SetToImageRange(const Image *image); /** * If a level window is set to fixed, the set and get methods won't accept * modifications to the level window settings anymore. This behaviour can * be turned of by setting fixed to false; */ void SetFixed(bool fixed); /** * Returns whether the level window settings are fixed (@see SetFixed(bool)) or not */ bool GetFixed() const; /** * Returns whether the level window settings are fixed (@see SetFixed(bool)) or not */ bool IsFixed() const; /*! * \brief equality operator implementation that allows to compare two level windows */ virtual bool operator==(const LevelWindow &levWin) const; /*! * \brief non equality operator implementation that allows to compare two level windows */ virtual bool operator!=(const LevelWindow &levWin) const; /*! * \brief implementation necessary because operator made * private in itk::Object */ virtual LevelWindow &operator=(const LevelWindow &levWin); /*! * \brief Shows if floating values are accepted */ bool IsFloatingValues() const; /*! * \brief Sets the floating image value */ void SetFloatingValues(bool value); protected: /*! * lower bound of current window */ ScalarType m_LowerWindowBound; /*! * upper bound of current window */ ScalarType m_UpperWindowBound; /*! * minimum gray value of the window */ ScalarType m_RangeMin; /*! * maximum gray value of the window */ ScalarType m_RangeMax; /*! * default minimum gray value of the window */ ScalarType m_DefaultLowerBound; /*! * default maximum gray value of the window */ ScalarType m_DefaultUpperBound; /*! * Image with floating values */ bool m_IsFloatingImage; /*! * Defines whether the level window settings may be changed after * initialization or not. */ bool m_Fixed; /*! * confidence tests * * if m_LowerWindowBound > m_UpperWindowBound, then the values for m_LowerWindowBound and m_UpperWindowBound will be * exchanged * * if m_LowerWindowBound < m_RangeMin, m_LowerWindowBound will be set to m_RangeMin. m_UpperWindowBound will be * decreased the same as m_LowerWindowBound will be increased, but minimum value for m_UpperWindowBound is also * m_RangeMin. * * if m_UpperWindowBound > m_RangeMax, m_UpperWindowBound will be set to m_RangeMax. m_LowerWindowBound will be * increased the same as m_UpperWindowBound will be decreased, but maximum value for m_LowerWindowBound is also * m_RangeMax. * */ 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 4c4325de22..30209a6f26 100755 --- a/Modules/Core/include/mitkLevelWindowProperty.h +++ b/Modules/Core/include/mitkLevelWindowProperty.h @@ -1,85 +1,88 @@ /*============================================================================ 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 mitkLevelWindowProperty_h #define mitkLevelWindowProperty_h #include "mitkBaseProperty.h" #include "mitkLevelWindow.h" namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief The LevelWindowProperty class Property for the mitk::LevelWindow * * @ingroup DataManagement * * @note If you want to apply the mitk::LevelWindowProperty to an mitk::Image, * make sure to set the mitk::RenderingModeProperty to a mode which supports * level window (e.g. LEVELWINDOW_COLOR). Make sure to check the documentation * of the mitk::RenderingModeProperty. For a code example how to use the * mitk::LevelWindowProperty check the mitkImageVtkMapper2DLevelWindowTest.cpp * in Core/Code/Testing. */ class MITKCORE_EXPORT LevelWindowProperty : public BaseProperty { protected: LevelWindow m_LevWin; LevelWindowProperty(); LevelWindowProperty(const LevelWindowProperty &other); LevelWindowProperty(const mitk::LevelWindow &levWin); public: mitkClassMacro(LevelWindowProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(LevelWindowProperty, const mitk::LevelWindow &); typedef LevelWindow ValueType; ~LevelWindowProperty() override; const mitk::LevelWindow &GetLevelWindow() const; const mitk::LevelWindow &GetValue() const; void SetLevelWindow(const LevelWindow &levWin); void SetValue(const ValueType &levWin); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: // purposely not implemented LevelWindowProperty &operator=(const LevelWindowProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkLookupTableProperty.h b/Modules/Core/include/mitkLookupTableProperty.h index 8a57cc0e83..3b72837858 100644 --- a/Modules/Core/include/mitkLookupTableProperty.h +++ b/Modules/Core/include/mitkLookupTableProperty.h @@ -1,84 +1,87 @@ /*============================================================================ 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 mitkLookupTableProperty_h #define mitkLookupTableProperty_h #include "mitkBaseProperty.h" #include "mitkLookupTable.h" #include <MitkCoreExports.h> namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief The LookupTableProperty class Property to associate mitk::LookupTable * to an mitk::DataNode. * @ingroup DataManagement * * @note If you want to use this property to colorize an mitk::Image, make sure * to set the mitk::RenderingModeProperty to a mode which supports lookup tables * (e.g. LOOKUPTABLE_COLOR). Make sure to check the documentation of the * mitk::RenderingModeProperty. For a code example how to use the mitk::LookupTable * and this property check the mitkImageVtkMapper2DLookupTableTest.cpp in * Core/Code/Testing. */ class MITKCORE_EXPORT LookupTableProperty : public BaseProperty { protected: LookupTable::Pointer m_LookupTable; LookupTableProperty(); LookupTableProperty(const LookupTableProperty &); LookupTableProperty(const mitk::LookupTable::Pointer lut); public: typedef LookupTable::Pointer ValueType; mitkClassMacro(LookupTableProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(LookupTableProperty, const mitk::LookupTable::Pointer); itkGetObjectMacro(LookupTable, LookupTable); ValueType GetValue() const; void SetLookupTable(const mitk::LookupTable::Pointer aLookupTable); void SetValue(const ValueType &); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: // purposely not implemented LookupTableProperty &operator=(const LookupTableProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkPoint.h b/Modules/Core/include/mitkPoint.h index 0436d5ae23..0f9c36682b 100644 --- a/Modules/Core/include/mitkPoint.h +++ b/Modules/Core/include/mitkPoint.h @@ -1,135 +1,153 @@ /*============================================================================ 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 mitkPoint_h #define mitkPoint_h #include <itkPoint.h> -#include "mitkArray.h" -#include "mitkEqual.h" -#include "mitkNumericConstants.h" +#include <mitkArray.h> +#include <mitkEqual.h> +#include <mitkNumericConstants.h> + +#include <nlohmann/json.hpp> namespace mitk { //##Documentation //##@brief enumeration of the type a point can be enum PointSpecificationType { PTUNDEFINED = 0, PTSTART, PTCORNER, PTEDGE, PTEND }; template <class TCoordRep, unsigned int NPointDimension = 3> class Point : public itk::Point<TCoordRep, NPointDimension> { public: /** Default constructor has nothing to do. */ explicit Point<TCoordRep, NPointDimension>() : itk::Point<TCoordRep, NPointDimension>() {} /** Pass-through constructors for the Array base class. */ template <typename TPointValueType> explicit Point(const Point<TPointValueType, NPointDimension> &r) : itk::Point<TCoordRep, NPointDimension>(r) { } template <typename TPointValueType> explicit Point(const TPointValueType r[NPointDimension]) : itk::Point<TCoordRep, NPointDimension>(r) { } template <typename TPointValueType> explicit Point(const TPointValueType &v) : itk::Point<TCoordRep, NPointDimension>(v) { } Point<TCoordRep, NPointDimension>(const mitk::Point<TCoordRep, NPointDimension> &r) : itk::Point<TCoordRep, NPointDimension>(r) { } Point<TCoordRep, NPointDimension>(const TCoordRep r[NPointDimension]) : itk::Point<TCoordRep, NPointDimension>(r) {} Point<TCoordRep, NPointDimension>(const TCoordRep &v) : itk::Point<TCoordRep, NPointDimension>(v) {} Point<TCoordRep, NPointDimension>(const itk::Point<TCoordRep, NPointDimension> &p) : itk::Point<TCoordRep, NPointDimension>(p) { } /** * Copies the elements from array array to this. * Note that this method will assign doubles to floats without complaining! * * @param array the array whose values shall be copied. Must overload [] operator. */ template <typename ArrayType> void FillPoint(const ArrayType &array) { itk::FixedArray<TCoordRep, NPointDimension> *thisP = dynamic_cast<itk::FixedArray<TCoordRep, NPointDimension> *>(this); mitk::FillArray<ArrayType, TCoordRep, NPointDimension>(*thisP, array); } /** * Copies the values stored in this point into the array array. * * @param array the array which should store the values of this. */ template <typename ArrayType> void ToArray(ArrayType array) const { mitk::ToArray<ArrayType, TCoordRep, NPointDimension>(array, *this); } }; + template <class TCoordRep, unsigned int NPointDimension> + void to_json(nlohmann::json& j, const Point<TCoordRep, NPointDimension>& p) + { + j = nlohmann::json::array(); + + for (size_t i = 0; i < NPointDimension; ++i) + j.push_back(p[i]); + } + + template <class TCoordRep, unsigned int NPointDimension> + void from_json(const nlohmann::json& j, Point<TCoordRep, NPointDimension>& p) + { + for (size_t i = 0; i < NPointDimension; ++i) + j.at(i).get_to(p[i]); + } + typedef Point<ScalarType, 2> Point2D; typedef Point<ScalarType, 3> Point3D; typedef Point<ScalarType, 4> Point4D; typedef Point<int, 2> Point2I; typedef Point<int, 3> Point3I; typedef Point<int, 4> Point4I; /** * @ingroup MITKTestingAPI * * @param point1 Point to compare. * @param point2 Point to compare. * @param eps Tolerance for floating point comparison. * @param verbose Flag indicating detailed console output. * @return True if points are equal. */ template <typename TCoordRep, unsigned int NPointDimension> inline bool Equal(const itk::Point<TCoordRep, NPointDimension> &point1, const itk::Point<TCoordRep, NPointDimension> &point2, TCoordRep eps = mitk::eps, bool verbose = false) { bool isEqual = true; typename itk::Point<TCoordRep, NPointDimension>::VectorType diff = point1 - point2; for (unsigned int i = 0; i < NPointDimension; i++) { if (DifferenceBiggerOrEqualEps(diff[i], eps)) { isEqual = false; break; } } ConditionalOutputOfDifference(point1, point2, eps, verbose, isEqual); return isEqual; } } // namespace mitk #endif diff --git a/Modules/Core/include/mitkPropertyDeserialization.h b/Modules/Core/include/mitkPropertyDeserialization.h new file mode 100644 index 0000000000..274bb1d22f --- /dev/null +++ b/Modules/Core/include/mitkPropertyDeserialization.h @@ -0,0 +1,41 @@ +/*============================================================================ + +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 <mitkIPropertyDeserialization.h> +#include <map> + +namespace mitk +{ + class PropertyDeserialization : public IPropertyDeserialization + { + public: + PropertyDeserialization(); + virtual ~PropertyDeserialization(); + + PropertyDeserialization(const PropertyDeserialization&) = delete; + PropertyDeserialization& operator=(const PropertyDeserialization&) = delete; + + itk::SmartPointer<BaseProperty> CreateInstance(const std::string& className) override; + + protected: + void InternalRegisterProperty(const BaseProperty* property) override; + + private: + using MapType = std::map<std::string, BaseProperty::ConstPointer>; + MapType m_Map; + }; +} + +#endif diff --git a/Modules/Core/include/mitkPropertyList.h b/Modules/Core/include/mitkPropertyList.h index 40f05857cf..763715f97b 100644 --- a/Modules/Core/include/mitkPropertyList.h +++ b/Modules/Core/include/mitkPropertyList.h @@ -1,254 +1,268 @@ /*============================================================================ 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 mitkPropertyList_h #define mitkPropertyList_h -#include "mitkBaseProperty.h" -#include "mitkGenericProperty.h" -#include "mitkUIDGenerator.h" -#include "mitkIPropertyOwner.h" -#include <MitkCoreExports.h> +#include <mitkIPropertyOwner.h> +#include <mitkGenericProperty.h> -#include <itkObjectFactory.h> - -#include <map> -#include <string> +#include <nlohmann/json_fwd.hpp> namespace mitk { - class XMLWriter; - /** * @brief Key-value list holding instances of BaseProperty * * This list is meant to hold an arbitrary list of "properties", * which should describe the object associated with this list. * * Usually you will use PropertyList as part of a DataNode * object - in this context the properties describe the data object * held by the DataNode (e.g. whether the object is rendered at * all, which color is used for rendering, what name should be * displayed for the object, etc.) * * The values in the list are not fixed, you may introduce any kind * of property that seems useful - all you have to do is inherit * from BaseProperty. * * The list is organized as a key-value pairs, i.e. * * \li "name" : pointer to a StringProperty * \li "visible" : pointer to a BoolProperty * \li "color" : pointer to a ColorProperty * \li "volume" : pointer to a FloatProperty * * Please see the documentation of SetProperty and ReplaceProperty for two * quite different semantics. Normally SetProperty is what you want - this * method will try to change the value of an existing property and will * not allow you to replace e.g. a ColorProperty with an IntProperty. * * Please also regard, that the key of a property must be a none empty string. * This is a precondition. Setting properties with empty keys will raise an exception. * * @ingroup DataManagement */ class MITKCORE_EXPORT PropertyList : public itk::Object, public IPropertyOwner { public: mitkClassMacroItkParent(PropertyList, itk::Object); /** * Method for creation through the object factory. */ itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Map structure to hold the properties: the map key is a string, * the value consists of the actual property object (BaseProperty). */ typedef std::map<std::string, BaseProperty::Pointer> PropertyMap; typedef std::pair<std::string, BaseProperty::Pointer> PropertyMapElementType; // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector<std::string> GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector<std::string> GetPropertyContextNames() const override; // IPropertyOwner 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; /** * @brief Get a property by its name. */ mitk::BaseProperty *GetProperty(const std::string &propertyKey) const; /** * @brief Set a property object in the list/map by reference. * * The actual OBJECT holding the value of the property is replaced by this function. * This is useful if you want to change the type of the property, like from BoolProperty to StringProperty. * Another use is to share one and the same property object among several PropertyList/DataNode objects, which * makes them appear synchronized. */ void ReplaceProperty(const std::string &propertyKey, BaseProperty *property); /** * @brief Set a property object in the list/map by reference. */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); //##Documentation //## @brief Convenience access method for GenericProperty<T> properties //## (T being the type of the second parameter) //## @return @a true property was found template <typename T> bool GetPropertyValue(const char *propertyKey, T &value) const { GenericProperty<T> *gp = dynamic_cast<GenericProperty<T> *>(GetProperty(propertyKey)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /** * @brief Convenience method to access the value of a BoolProperty */ bool GetBoolProperty(const char *propertyKey, bool &boolValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, bool &boolValue) const; /** * @brief Convenience method to set the value of a BoolProperty */ void SetBoolProperty(const char *propertyKey, bool boolValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, bool boolValue); /** * @brief Convenience method to access the value of an IntProperty */ bool GetIntProperty(const char *propertyKey, int &intValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, int &intValue) const; /** * @brief Convenience method to set the value of an IntProperty */ void SetIntProperty(const char *propertyKey, int intValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, int intValue); /** * @brief Convenience method to access the value of a FloatProperty */ bool GetFloatProperty(const char *propertyKey, float &floatValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, float &floatValue) const; /** * @brief Convenience method to set the value of a FloatProperty */ void SetFloatProperty(const char *propertyKey, float floatValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, float floatValue); /** * @brief Convenience method to access the value of a DoubleProperty */ bool GetDoubleProperty(const char *propertyKey, double &doubleValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, double &doubleValue) const; /** * @brief Convenience method to set the value of a DoubleProperty */ void SetDoubleProperty(const char *propertyKey, double doubleValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, double doubleValue); /** * @brief Convenience method to access the value of a StringProperty */ bool GetStringProperty(const char *propertyKey, std::string &stringValue) const; /** * @brief ShortCut for the above method */ bool Get(const char *propertyKey, std::string &stringValue) const; /** * @brief Convenience method to set the value of a StringProperty */ void SetStringProperty(const char *propertyKey, const char *stringValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, const char *stringValue); /** * @brief ShortCut for the above method */ void Set(const char *propertyKey, const std::string &stringValue); /** * @brief Get the timestamp of the last change of the map or the last change of one of * the properties store in the list (whichever is later). */ itk::ModifiedTimeType GetMTime() const override; /** * @brief Remove a property from the list/map. */ bool DeleteProperty(const std::string &propertyKey); const PropertyMap *GetMap() const { return &m_Properties; } 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: PropertyList(); PropertyList(const PropertyList &other); ~PropertyList() override; /** * @brief Map of properties. */ PropertyMap m_Properties; private: itk::LightObject::Pointer InternalClone() const override; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkSmartPointerProperty.h b/Modules/Core/include/mitkSmartPointerProperty.h index aac5f37d69..ddf84f66b3 100644 --- a/Modules/Core/include/mitkSmartPointerProperty.h +++ b/Modules/Core/include/mitkSmartPointerProperty.h @@ -1,98 +1,101 @@ /*============================================================================ 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 mitkSmartPointerProperty_h #define mitkSmartPointerProperty_h #include "mitkBaseProperty.h" #include "mitkUIDGenerator.h" #include <MitkCoreExports.h> #include <list> #include <map> #include <string> namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif //##Documentation //## @brief Property containing a smart-pointer //## @ingroup DataManagement class MITKCORE_EXPORT SmartPointerProperty : public BaseProperty { public: mitkClassMacro(SmartPointerProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(SmartPointerProperty, itk::Object*); typedef itk::Object::Pointer ValueType; itk::Object::Pointer GetSmartPointer() const; ValueType GetValue() const; void SetSmartPointer(itk::Object *); void SetValue(const ValueType &); /// mainly for XML output std::string GetValueAsString() const override; static void PostProcessXMLReading(); /// Return the number of SmartPointerProperties that reference the object given as parameter static unsigned int GetReferenceCountFor(itk::Object *); 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: SmartPointerProperty(itk::Object * = nullptr); SmartPointerProperty(const SmartPointerProperty &); itk::Object::Pointer m_SmartPointer; private: // purposely not implemented SmartPointerProperty &operator=(const SmartPointerProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &) const override; bool Assign(const BaseProperty &) override; typedef std::map<itk::Object *, unsigned int> ReferenceCountMapType; typedef std::map<itk::Object *, std::string> ReferencesUIDMapType; typedef std::map<SmartPointerProperty *, std::string> ReadInSmartPointersMapType; typedef std::map<std::string, itk::Object *> ReadInTargetsMapType; /// for each itk::Object* count how many SmartPointerProperties point to it static ReferenceCountMapType m_ReferenceCount; static ReferencesUIDMapType m_ReferencesUID; static ReadInSmartPointersMapType m_ReadInInstances; static ReadInTargetsMapType m_ReadInTargets; /// to generate unique IDs for the objects pointed at (during XML writing) static UIDGenerator m_UIDGenerator; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkStringProperty.h b/Modules/Core/include/mitkStringProperty.h index 04e7d744af..41962df4e1 100644 --- a/Modules/Core/include/mitkStringProperty.h +++ b/Modules/Core/include/mitkStringProperty.h @@ -1,78 +1,81 @@ /*============================================================================ 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 mitkStringProperty_h #define mitkStringProperty_h #include <itkConfigure.h> #include "mitkBaseProperty.h" #include <MitkCoreExports.h> #include <string> namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief Property for strings * @ingroup DataManagement */ class MITKCORE_EXPORT StringProperty : public BaseProperty { protected: std::string m_Value; StringProperty(const char *string = nullptr); StringProperty(const std::string &s); StringProperty(const StringProperty &); public: mitkClassMacro(StringProperty, BaseProperty); typedef std::string ValueType; itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(StringProperty, const char*); mitkNewMacro1Param(StringProperty, const std::string&); itkGetStringMacro(Value); itkSetStringMacro(Value); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + static const char *PATH; using BaseProperty::operator=; private: // purposely not implemented StringProperty &operator=(const StringProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkTemporoSpatialStringProperty.h b/Modules/Core/include/mitkTemporoSpatialStringProperty.h index 595c8d1aca..8f6f090f93 100644 --- a/Modules/Core/include/mitkTemporoSpatialStringProperty.h +++ b/Modules/Core/include/mitkTemporoSpatialStringProperty.h @@ -1,136 +1,139 @@ /*============================================================================ 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 mitkTemporoSpatialStringProperty_h #define mitkTemporoSpatialStringProperty_h #include <itkConfigure.h> #include "mitkBaseProperty.h" #include <MitkCoreExports.h> #include "mitkTimeGeometry.h" #include <string> namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief Property for time and space resolved string values * @ingroup DataManagement */ class MITKCORE_EXPORT TemporoSpatialStringProperty : public BaseProperty { public: typedef ::itk::IndexValueType IndexValueType; typedef std::string ValueType; mitkClassMacro(TemporoSpatialStringProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(TemporoSpatialStringProperty, const char*); mitkNewMacro1Param(TemporoSpatialStringProperty, const std::string &); /**Returns the value of the first time point in the first slice. * If now value is set it returns an empty string.*/ ValueType GetValue() const; /**Returns the value of the passed time step and slice. If it does not exist and allowedClosed is true * it will look for the closest value. If nothing could be found an empty string will be returned.*/ ValueType GetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime = false, bool allowCloseSlice = false) const; ValueType GetValueBySlice(const IndexValueType &zSlice, bool allowClose = false) const; ValueType GetValueByTimeStep(const TimeStepType &timeStep, bool allowClose = false) const; bool HasValue() const; bool HasValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime = false, bool allowCloseSlice = false) const; bool HasValueBySlice(const IndexValueType &zSlice, bool allowClose = false) const; bool HasValueByTimeStep(const TimeStepType &timeStep, bool allowClose = false) const; /** return all slices stored for the specified timestep.*/ std::vector<IndexValueType> GetAvailableSlices(const TimeStepType& timeStep) const; /** return all time steps stored for the specified slice.*/ std::vector<TimeStepType> GetAvailableTimeSteps(const IndexValueType& slice) const; /** return all time steps stored in the property.*/ std::vector<TimeStepType> GetAvailableTimeSteps() const; /** return all slices stored in the property. @remark not all time steps may contain all slices.*/ std::vector<IndexValueType> GetAvailableSlices() const; void SetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, const ValueType &value); void SetValue(const ValueType &value); std::string GetValueAsString() const override; /** Indicates of all values (all time steps, all slices) are the same, or if at least one value stored in the property is different. If IsUniform==true one can i.a. use GetValueAsString() without the loss of 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: typedef std::map<IndexValueType, std::string> SliceMapType; typedef std::map<TimeStepType, SliceMapType> TimeMapType; TimeMapType m_Values; TemporoSpatialStringProperty(const char *string = nullptr); TemporoSpatialStringProperty(const std::string &s); TemporoSpatialStringProperty(const TemporoSpatialStringProperty &); std::pair<bool, ValueType> CheckValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime = false, bool allowCloseSlice = false) const; private: // purposely not implemented TemporoSpatialStringProperty &operator=(const TemporoSpatialStringProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; 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 { /**Deserialize a passed JSON string into a TemporoSpatialStringProperty.*/ MITKCORE_EXPORT mitk::BaseProperty::Pointer deserializeJSONToTemporoSpatialStringProperty(const std::string &value); } #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkTransferFunctionProperty.h b/Modules/Core/include/mitkTransferFunctionProperty.h index e8bf9439cc..fd3c20d736 100644 --- a/Modules/Core/include/mitkTransferFunctionProperty.h +++ b/Modules/Core/include/mitkTransferFunctionProperty.h @@ -1,79 +1,82 @@ /*============================================================================ 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 mitkTransferFunctionProperty_h #define mitkTransferFunctionProperty_h #include "mitkBaseProperty.h" #include "mitkTransferFunction.h" namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief The TransferFunctionProperty class Property class for the mitk::TransferFunction. * @ingroup DataManagement * * @note If you want to use this property for an mitk::Image, make sure * to set the mitk::RenderingModeProperty to a mode which supports transfer * functions (e.g. COLORTRANSFERFUNCTION_COLOR). Make sure to check the * documentation of the mitk::RenderingModeProperty. For a code example how * to use the mitk::TransferFunction check the * mitkImageVtkMapper2DTransferFunctionTest.cpp in Core/Code/Testing. */ class MITKCORE_EXPORT TransferFunctionProperty : public BaseProperty { public: typedef mitk::TransferFunction::Pointer ValueType; mitkClassMacro(TransferFunctionProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self) mitkNewMacro1Param(TransferFunctionProperty, mitk::TransferFunction::Pointer); itkSetMacro(Value, mitk::TransferFunction::Pointer); itkGetConstMacro(Value, mitk::TransferFunction::Pointer); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: mitk::TransferFunction::Pointer m_Value; TransferFunctionProperty(); TransferFunctionProperty(const TransferFunctionProperty &other); TransferFunctionProperty(mitk::TransferFunction::Pointer value); private: // purposely not implemented TransferFunctionProperty &operator=(const TransferFunctionProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/include/mitkVector.h b/Modules/Core/include/mitkVector.h index fca4c9047d..6c5bc2ddaf 100644 --- a/Modules/Core/include/mitkVector.h +++ b/Modules/Core/include/mitkVector.h @@ -1,239 +1,257 @@ /*============================================================================ 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 mitkVector_h #define mitkVector_h #include <itkVector.h> #include <vnl/vnl_vector.h> #include <vnl/vnl_vector_fixed.h> -#include "mitkArray.h" -#include "mitkEqual.h" -#include "mitkExceptionMacro.h" -#include "mitkNumericConstants.h" +#include <mitkArray.h> +#include <mitkEqual.h> +#include <mitkExceptionMacro.h> +#include <mitkNumericConstants.h> + +#include <nlohmann/json.hpp> namespace mitk { template <class TCoordRep, unsigned int NVectorDimension = 3> class Vector : public itk::Vector<TCoordRep, NVectorDimension> { public: /** * @brief Default constructor has nothing to do. */ explicit Vector<TCoordRep, NVectorDimension>() : itk::Vector<TCoordRep, NVectorDimension>() {} /** * @brief Copy constructor. */ explicit Vector<TCoordRep, NVectorDimension>(const mitk::Vector<TCoordRep, NVectorDimension> &r) : itk::Vector<TCoordRep, NVectorDimension>(r) { } /** Pass-through assignment operator for the Vector base class. */ Vector<TCoordRep, NVectorDimension> & operator=(const Vector<TCoordRep, NVectorDimension> & r) { itk::Vector<TCoordRep, NVectorDimension>::operator=(r); return *this; } /** * @brief Constructor to convert from itk::Vector to mitk::Vector. */ Vector<TCoordRep, NVectorDimension>(const itk::Vector<TCoordRep, NVectorDimension> &r) : itk::Vector<TCoordRep, NVectorDimension>(r) { } /** * @brief Constructor to convert an array to mitk::Vector * @param r the array. * @attention must have NVectorDimension valid arguments! */ Vector<TCoordRep, NVectorDimension>(const TCoordRep r[NVectorDimension]) : itk::Vector<TCoordRep, NVectorDimension>(r) { } /** * Constructor to initialize entire vector to one value. */ Vector<TCoordRep, NVectorDimension>(const TCoordRep &v) : itk::Vector<TCoordRep, NVectorDimension>(v) {} /** * @brief Constructor for vnl_vectors. * @throws mitk::Exception if vnl_vector.size() != NVectorDimension. */ Vector<TCoordRep, NVectorDimension>(const vnl_vector<TCoordRep> &vnlVector) : itk::Vector<TCoordRep, NVectorDimension>() { if (vnlVector.size() != NVectorDimension) mitkThrow() << "when constructing mitk::Vector from vnl_vector: sizes didn't match: mitk::Vector " << NVectorDimension << "; vnl_vector " << vnlVector.size(); for (unsigned int var = 0; (var < NVectorDimension) && (var < vnlVector.size()); ++var) { this->SetElement(var, vnlVector.get(var)); } } /** * @brief Constructor for vnl_vector_fixed. */ Vector<TCoordRep, NVectorDimension>(const vnl_vector_fixed<TCoordRep, NVectorDimension> &vnlVectorFixed) : itk::Vector<TCoordRep, NVectorDimension>() { for (unsigned int var = 0; var < NVectorDimension; ++var) { this->SetElement(var, vnlVectorFixed[var]); } }; /** * Copies the elements from array array to this. * Note that this method will assign doubles to floats without complaining! * * @param array the array whose values shall be copied. Must overload [] operator. */ template <typename ArrayType> void FillVector(const ArrayType &array) { itk::FixedArray<TCoordRep, NVectorDimension> *thisP = dynamic_cast<itk::FixedArray<TCoordRep, NVectorDimension> *>(this); mitk::FillArray<ArrayType, TCoordRep, NVectorDimension>(*thisP, array); } /** * Copies the values stored in this vector into the array array.d * * @param array the array which should store the values of this. */ template <typename ArrayType> void ToArray(ArrayType array) const { mitk::ToArray<ArrayType, TCoordRep, NVectorDimension>(array, *this); } /** * @brief User defined conversion of mitk::Vector to vnl_vector. * Note: the conversion to mitk::Vector to vnl_vector_fixed has not been implemented since this * would collide with the conversion vnl_vector to vnl_vector_fixed provided by vnl. */ operator vnl_vector<TCoordRep>() const { return this->GetVnlVector(); } }; // end mitk::Vector + template <class TCoordRep, unsigned int NVectorDimension> + void to_json(nlohmann::json &j, const Vector<TCoordRep, NVectorDimension> &v) + { + j = nlohmann::json::array(); + + for (size_t i = 0; i < NVectorDimension; ++i) + j.push_back(v[i]); + } + + template <class TCoordRep, unsigned int NVectorDimension> + void from_json(const nlohmann::json &j, Vector<TCoordRep, NVectorDimension> &v) + { + for (size_t i = 0; i < NVectorDimension; ++i) + j.at(i).get_to(v[i]); + } + // convenience typedefs for often used mitk::Vector representations. typedef Vector<ScalarType, 2> Vector2D; typedef Vector<ScalarType, 3> Vector3D; typedef Vector<ScalarType, 4> Vector4D; // other vector types used in MITK typedef vnl_vector<ScalarType> VnlVector; // The equal methods to compare vectors for equality are below: /** * @ingroup MITKTestingAPI * * @param vector1 Vector to compare. * @param vector2 Vector to compare. * @param eps Tolerance for floating point comparison. * @param verbose Flag indicating detailed console output. * @return True if vectors are equal. */ template <typename TCoordRep, unsigned int NPointDimension> inline bool Equal(const itk::Vector<TCoordRep, NPointDimension> &vector1, const itk::Vector<TCoordRep, NPointDimension> &vector2, TCoordRep eps = mitk::eps, bool verbose = false) { bool isEqual = true; typename itk::Vector<TCoordRep, NPointDimension>::VectorType diff = vector1 - vector2; for (unsigned int i = 0; i < NPointDimension; i++) { if (DifferenceBiggerOrEqualEps(diff[i], eps)) { isEqual = false; break; } } ConditionalOutputOfDifference(vector1, vector2, eps, verbose, isEqual); return isEqual; } /** * @ingroup MITKTestingAPI * * @param vector1 Vector to compare. * @param vector2 Vector to compare. * @param eps Tolerance for floating point comparison. * @param verbose Flag indicating detailed console output. * @return True if vectors are equal. */ inline bool Equal(const mitk::VnlVector &vector1, const mitk::VnlVector &vector2, ScalarType eps = mitk::eps, bool verbose = false) { bool isEqual = true; mitk::VnlVector diff = vector1 - vector2; for (unsigned int i = 0; i < diff.size(); i++) { if (DifferenceBiggerOrEqualEps(diff[i], eps)) { isEqual = false; break; } } ConditionalOutputOfDifference(vector1, vector2, eps, verbose, isEqual); return isEqual; } /** * @ingroup MITKTestingAPI * * @param vector1 Vector to compare. * @param vector2 Vector to compare. * @param eps Tolerance for floating point comparison. * @param verbose Flag indicating detailed console output. * @return True if vectors are equal. */ template <typename TCoordRep, unsigned int NPointDimension> inline bool Equal(const vnl_vector_fixed<TCoordRep, NPointDimension> &vector1, const vnl_vector_fixed<TCoordRep, NPointDimension> &vector2, TCoordRep eps = mitk::eps, bool verbose = false) { vnl_vector_fixed<TCoordRep, NPointDimension> diff = vector1 - vector2; bool isEqual = true; for (unsigned int i = 0; i < diff.size(); i++) { if (DifferenceBiggerOrEqualEps(diff[i], eps)) { isEqual = false; break; } } ConditionalOutputOfDifference(vector1, vector2, eps, verbose, isEqual); return isEqual; } } // end namespace mitk #endif diff --git a/Modules/Core/include/mitkVectorProperty.h b/Modules/Core/include/mitkVectorProperty.h index be0b00f767..547a5d5caf 100644 --- a/Modules/Core/include/mitkVectorProperty.h +++ b/Modules/Core/include/mitkVectorProperty.h @@ -1,133 +1,136 @@ /*============================================================================ 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 mitkVectorProperty_h #define mitkVectorProperty_h // MITK #include <MitkCoreExports.h> #include <mitkBaseProperty.h> // STL #include <vector> namespace mitk { /** \brief Helper for VectorProperty to determine a good ITK ClassName. This template is specialized for special instantiations that need a serializer of this VectorProperty. */ template <typename D> struct VectorPropertyDataType { static const char *prefix() { return "Invalid"; } }; /** \brief Providing a std::vector as property. Templated over the data type of the std::vector that is held by this class. Nothing special about data handling, setting and getting of std::vectors is implemented by-value. When checking the declaration of this class, you'll notice that it does not use the mitkClassMacro but instead writes all of its definition manually. This is in order to specifically override the GetNameOfClass() method without having to inherit again from the template (see comments in code). */ template <typename DATATYPE> class MITKCORE_EXPORT VectorProperty : public BaseProperty { public: typedef std::vector<DATATYPE> VectorType; // Manually expand most of mitkClassMacro: // mitkClassMacro(VectorProperty<DATATYPE>, mitk::BaseProperty); // This manual expansion is done to override explicitly // the GetNameOfClass() and GetStaticNameOfClass() methods typedef VectorProperty<DATATYPE> Self; typedef BaseProperty SuperClass; typedef itk::SmartPointer<Self> Pointer; typedef itk::SmartPointer<const Self> ConstPointer; std::vector<std::string> GetClassHierarchy() const override { return mitk::GetClassHierarchy<Self>(); } /// This function must return different /// strings in function of the template parameter! /// Serialization depends on this feature. static const char *GetStaticNameOfClass() { // concatenate a prefix dependent on the template type and our own classname static std::string nameOfClass = std::string(VectorPropertyDataType<DATATYPE>::prefix()) + "VectorProperty"; return nameOfClass.c_str(); } const char *GetNameOfClass() const override { return this->GetStaticNameOfClass(); } itkFactorylessNewMacro(Self); itkCloneMacro(Self); /// Returns the property value as a std::string. /// /// Since VectorProperty potentially holds many /// elements, it implements this function in a way /// that only the first and the last couple of /// elements really appear in the string. /// Missing central elements are indicated by /// an ellipsis ("...") std::string GetValueAsString() const override; /// returns a const reference to the contained vector virtual const VectorType &GetValue() const; /// 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 &); /// creates a copy of it self itk::LightObject::Pointer InternalClone() const override; /// compares two properties. bool IsEqual(const BaseProperty &an_other_property) const override; /// assigns the content of an_other_property to this bool Assign(const BaseProperty &an_other_property) override; /// property content VectorType m_PropertyContent; }; /// This should be used in .h files. #define MITK_DECLARE_VECTOR_PROPERTY(TYPE, PREFIX) \ \ typedef VectorProperty<TYPE> PREFIX##VectorProperty; \ \ template <> \ \ struct VectorPropertyDataType<TYPE> \ { \ static const char *prefix() { return #PREFIX; } \ }; /// This should be used in a .cpp file #define MITK_DEFINE_VECTOR_PROPERTY(TYPE) template class VectorProperty<TYPE>; MITK_DECLARE_VECTOR_PROPERTY(double, Double) MITK_DECLARE_VECTOR_PROPERTY(int, Int) } // namespace #endif diff --git a/Modules/Core/include/mitkWeakPointerProperty.h b/Modules/Core/include/mitkWeakPointerProperty.h index aba93c9c37..db11e06a19 100644 --- a/Modules/Core/include/mitkWeakPointerProperty.h +++ b/Modules/Core/include/mitkWeakPointerProperty.h @@ -1,78 +1,81 @@ /*============================================================================ 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 mitkWeakPointerProperty_h #define mitkWeakPointerProperty_h #include "itkWeakPointer.h" #include "mitkBaseProperty.h" #include <MitkCoreExports.h> namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif //##Documentation //## @brief Property containing a smart-pointer //## //## @ingroup DataManagement class MITKCORE_EXPORT WeakPointerProperty : public BaseProperty { public: mitkClassMacro(WeakPointerProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(WeakPointerProperty, itk::Object*); ~WeakPointerProperty() override; typedef itk::WeakPointer<itk::Object> ValueType; ValueType GetWeakPointer() const; ValueType GetValue() const; void SetWeakPointer(itk::Object *pointer); void SetValue(const ValueType &value); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; protected: itk::WeakPointer<itk::Object> m_WeakPointer; WeakPointerProperty(const WeakPointerProperty &); WeakPointerProperty(itk::Object *pointer = nullptr); private: // purposely not implemented WeakPointerProperty &operator=(const WeakPointerProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp b/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp index 303e0c80f0..d3463856e3 100644 --- a/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkAnnotationProperty.cpp @@ -1,98 +1,114 @@ /*============================================================================ 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 "mitkAnnotationProperty.h" mitk::AnnotationProperty::AnnotationProperty() : m_Position(0.0) { } mitk::AnnotationProperty::AnnotationProperty(const char *label, const Point3D &position) : m_Label(""), m_Position(position) { if (label != nullptr) { m_Label = label; } } mitk::AnnotationProperty::AnnotationProperty(const std::string &label, const Point3D &position) : m_Label(label), m_Position(position) { } mitk::AnnotationProperty::AnnotationProperty(const char *label, ScalarType x, ScalarType y, ScalarType z) : m_Label("") { if (label != nullptr) { m_Label = label; } m_Position[0] = x; m_Position[1] = y; m_Position[2] = z; } mitk::AnnotationProperty::AnnotationProperty(const std::string &label, ScalarType x, ScalarType y, ScalarType z) : m_Label(label) { m_Position[0] = x; m_Position[1] = y; m_Position[2] = z; } mitk::AnnotationProperty::AnnotationProperty(const mitk::AnnotationProperty &other) : BaseProperty(other), m_Label(other.m_Label), m_Position(other.m_Position) { } const mitk::Point3D &mitk::AnnotationProperty::GetPosition() const { return m_Position; } void mitk::AnnotationProperty::SetPosition(const mitk::Point3D &position) { if (m_Position != position) { m_Position = position; this->Modified(); } } bool mitk::AnnotationProperty::IsEqual(const BaseProperty &property) const { return ((this->m_Label == static_cast<const Self &>(property).m_Label) && (this->m_Position == static_cast<const Self &>(property).m_Position)); } bool mitk::AnnotationProperty::Assign(const BaseProperty &property) { this->m_Label = static_cast<const Self &>(property).m_Label; this->m_Position = static_cast<const Self &>(property).m_Position; return true; } std::string mitk::AnnotationProperty::GetValueAsString() const { std::stringstream myStr; myStr << this->GetLabel() << this->GetPosition(); return myStr.str(); } itk::LightObject::Pointer mitk::AnnotationProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); 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<std::string>()); + this->SetPosition(j["Position"].get<Point3D>()); + return true; +} diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index 6a3a8352e7..183e402aad 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -1,1185 +1,1190 @@ /*============================================================================ 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 <iomanip> #include <sstream> #include <bitset> #include <vtkMatrix4x4.h> #include <vtkMatrixToLinearTransform.h> #include "mitkApplyTransformMatrixOperation.h" #include "mitkBaseGeometry.h" #include "mitkGeometryTransformHolder.h" #include "mitkInteractionConst.h" #include "mitkMatrixConvert.h" #include "mitkModifiedLock.h" #include "mitkPointOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkScaleOperation.h" #include "mitkVector.h" #include "mitkMatrix.h" mitk::BaseGeometry::BaseGeometry() : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0), m_ImageGeometry(false), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(); Initialize(); } mitk::BaseGeometry::BaseGeometry(const BaseGeometry &other) : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_ImageGeometry(other.m_ImageGeometry), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(*other.GetGeometryTransformHolder()); other.InitializeGeometry(this); } mitk::BaseGeometry::~BaseGeometry() { delete m_GeometryTransform; } void mitk::BaseGeometry::SetVtkMatrixDeepCopy(vtkTransform *vtktransform) { m_GeometryTransform->SetVtkMatrixDeepCopy(vtktransform); } const mitk::Point3D mitk::BaseGeometry::GetOrigin() const { return m_GeometryTransform->GetOrigin(); } void mitk::BaseGeometry::SetOrigin(const Point3D &origin) { mitk::ModifiedLock lock(this); if (origin != GetOrigin()) { m_GeometryTransform->SetOrigin(origin); Modified(); } } const mitk::Vector3D mitk::BaseGeometry::GetSpacing() const { return m_GeometryTransform->GetSpacing(); } void mitk::BaseGeometry::Initialize() { float b[6] = {0, 1, 0, 1, 0, 1}; SetFloatBounds(b); m_GeometryTransform->Initialize(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::BaseGeometry::SetFloatBounds(const float bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } void mitk::BaseGeometry::SetFloatBounds(const double bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const double *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } /** Initialize the geometry */ void mitk::BaseGeometry::InitializeGeometry(BaseGeometry *newGeometry) const { newGeometry->SetBounds(m_BoundingBox->GetBounds()); newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->InitializeGeometryTransformHolder(this); newGeometry->m_ImageGeometry = m_ImageGeometry; } void mitk::BaseGeometry::InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry) { this->m_GeometryTransform->Initialize(otherGeometry->GetGeometryTransformHolder()); } /** Set the bounds */ void mitk::BaseGeometry::SetBounds(const BoundsArrayType &bounds) { mitk::ModifiedLock lock(this); this->CheckBounds(bounds); m_BoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < m_NDimensions; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_BoundingBox->SetPoints(pointscontainer); m_BoundingBox->ComputeBoundingBox(); this->Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) { mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransform(transform); Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform) { // security check mitk::Vector3D originalSpacing = this->GetSpacing(); mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransformWithoutChangingSpacing(transform); Modified(); // Security check. Spacig must not have changed if (!mitk::Equal(originalSpacing, this->GetSpacing())) { MITK_WARN << "Spacing has changed in a method, where the spacing must not change."; assert(false); } } const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } bool mitk::BaseGeometry::IsValid() const { return true; } void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { PreSetSpacing(aSpacing); _SetSpacing(aSpacing, enforceSetSpacing); } void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { m_GeometryTransform->SetSpacing(aSpacing, enforceSetSpacing); } mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const { Vector3D frontToBack; frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).as_ref()); frontToBack *= GetExtent(direction); return frontToBack; } mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const { assert(m_BoundingBox.IsNotNull()); if (direction >= m_NDimensions) mitkThrow() << "Direction is too big. This geometry is for 3D Data"; BoundsArrayType bounds = m_BoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } bool mitk::BaseGeometry::Is2DConvertable() { bool isConvertableWithoutLoss = true; do { if (this->GetSpacing()[2] != 1) { isConvertableWithoutLoss = false; break; } if (this->GetOrigin()[2] != 0) { isConvertableWithoutLoss = false; break; } mitk::Vector3D col0, col1, col2; col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).as_ref()); col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).as_ref()); col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).as_ref()); if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) { isConvertableWithoutLoss = false; break; } } while (false); return isConvertableWithoutLoss; } mitk::Point3D mitk::BaseGeometry::GetCenter() const { assert(m_BoundingBox.IsNotNull()); Point3D c = m_BoundingBox->GetCenter(); if (m_ImageGeometry) { // Get Center returns the middel of min and max pixel index. In corner based images, this is the right position. // In center based images (imageGeometry == true), the index needs to be shifted back. c[0] -= 0.5; c[1] -= 0.5; c[2] -= 0.5; } this->IndexToWorld(c, c); return c; } double mitk::BaseGeometry::GetDiagonalLength2() const { Vector3D diagonalvector = GetCornerPoint() - GetCornerPoint(false, false, false); return diagonalvector.GetSquaredNorm(); } double mitk::BaseGeometry::GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const { assert(id >= 0); assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; switch (id) { case 0: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[4]); break; case 1: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[5]); break; case 2: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[4]); break; case 3: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[5]); break; case 4: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[4]); break; case 5: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[5]); break; case 6: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[4]); break; case 7: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[5]); break; default: { itkExceptionMacro(<< "A cube only has 8 corners. These are labeled 0-7."); } } if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).magnitude() * GetExtent(direction); } void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM) { mitk::ModifiedLock lock(this); ScalarType len = GetExtentInMM(direction); if (fabs(len - extentInMM) >= mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_GeometryTransform->GetVnlMatrix(); if (len > extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) / len * extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) * extentInMM / len); Matrix3D matrix; matrix = vnlmatrix; m_GeometryTransform->SetMatrix(matrix); Modified(); } } bool mitk::BaseGeometry::IsInside(const mitk::Point3D &p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } bool mitk::BaseGeometry::IsIndexInside(const mitk::Point3D &index) const { bool inside = false; // if it is an image geometry, we need to convert the index to discrete values // this is done by applying the rounding function also used in WorldToIndex (see line 323) if (m_ImageGeometry) { mitk::Point3D discretIndex; discretIndex[0] = itk::Math::RoundHalfIntegerUp<mitk::ScalarType>(index[0]); discretIndex[1] = itk::Math::RoundHalfIntegerUp<mitk::ScalarType>(index[1]); discretIndex[2] = itk::Math::RoundHalfIntegerUp<mitk::ScalarType>(index[2]); inside = this->GetBoundingBox()->IsInside(discretIndex); // we have to check if the index is at the upper border of each dimension, // because the boundingbox is not centerbased if (inside) { const BoundingBox::BoundsArrayType &bounds = this->GetBoundingBox()->GetBounds(); if ((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5])) inside = false; } } else inside = this->GetBoundingBox()->IsInside(index); return inside; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { mitk::Vector3D tempIn, tempOut; const TransformType::OffsetType &offset = this->GetIndexToWorldTransform()->GetOffset(); tempIn = pt_mm.GetVectorFromOrigin() - offset; WorldToIndex(tempIn, tempOut); pt_units = Point3D(tempOut); } void mitk::BaseGeometry::WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != this->GetIndexToWorldTransform()->GetMTime()) { if (!m_InvertedTransform) { m_InvertedTransform = TransformType::New(); } if (!this->GetIndexToWorldTransform()->GetInverse(m_InvertedTransform.GetPointer())) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed."); } m_IndexToWorldTransformLastModified = this->GetIndexToWorldTransform()->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType &inverse = m_InvertedTransform->GetMatrix(); if (inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << this->GetIndexToWorldTransform()->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse); } vec_units = inverse * vec_mm; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::WorldToIndex(point, vec, vec). Use " "BaseGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } mitk::VnlVector mitk::BaseGeometry::GetOriginVnl() const { return GetOrigin().GetVnlVector(); } vtkLinearTransform *mitk::BaseGeometry::GetVtkTransform() const { return m_GeometryTransform->GetVtkTransform(); } void mitk::BaseGeometry::SetIdentity() { mitk::ModifiedLock lock(this); m_GeometryTransform->SetIdentity(); Modified(); } void mitk::BaseGeometry::Compose(const mitk::BaseGeometry::TransformType *other, bool pre) { mitk::ModifiedLock lock(this); m_GeometryTransform->Compose(other, pre); Modified(); } void mitk::BaseGeometry::Compose(const vtkMatrix4x4 *vtkmatrix, bool pre) { mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } void mitk::BaseGeometry::Translate(const Vector3D &vector) { if ((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { this->SetOrigin(this->GetOrigin() + vector); } } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = this->GetIndexToWorldTransform()->TransformPoint(pt_units); } void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = this->GetIndexToWorldTransform()->TransformVector(vec_units); } void mitk::BaseGeometry::ExecuteOperation(Operation *operation) { mitk::ModifiedLock lock(this); vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { auto *pointOp = dynamic_cast<mitk::PointOperation *>(operation); if (pointOp == nullptr) { MITK_ERROR << "Point move operation is null!"; return; } mitk::Point3D newPos = pointOp->GetPoint(); ScalarType data[3]; vtktransform->GetPosition(data); vtktransform->PostMultiply(); vtktransform->Translate(newPos[0], newPos[1], newPos[2]); vtktransform->PreMultiply(); break; } case OpSCALE: { auto *scaleOp = dynamic_cast<mitk::ScaleOperation *>(operation); if (scaleOp == nullptr) { MITK_ERROR << "Scale operation is null!"; return; } mitk::Point3D newScale = scaleOp->GetScaleFactor(); ScalarType scalefactor[3]; scalefactor[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); scalefactor[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); scalefactor[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D anchor = scaleOp->GetScaleAnchorPoint(); vtktransform->PostMultiply(); vtktransform->Translate(-anchor[0], -anchor[1], -anchor[2]); vtktransform->Scale(scalefactor[0], scalefactor[1], scalefactor[2]); vtktransform->Translate(anchor[0], anchor[1], anchor[2]); break; } case OpROTATE: { auto *rotateOp = dynamic_cast<mitk::RotationOperation *>(operation); if (rotateOp == nullptr) { MITK_ERROR << "Rotation operation is null!"; return; } Vector3D rotationVector = rotateOp->GetVectorOfRotation(); Point3D center = rotateOp->GetCenterOfRotation(); ScalarType angle = rotateOp->GetAngleOfRotation(); vtktransform->PostMultiply(); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtktransform->Translate(center[0], center[1], center[2]); vtktransform->PreMultiply(); break; } case OpRESTOREPLANEPOSITION: { // Copy necessary to avoid vtk warning vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); TransferItkTransformToVtkMatrix( dynamic_cast<mitk::RestorePlanePositionOperation *>(operation)->GetTransform().GetPointer(), matrix); vtktransform->SetMatrix(matrix); matrix->Delete(); break; } case OpAPPLYTRANSFORMMATRIX: { auto *applyMatrixOp = dynamic_cast<ApplyTransformMatrixOperation *>(operation); vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); break; } default: vtktransform->Delete(); return; } this->SetVtkMatrixDeepCopy(vtktransform); Modified(); vtktransform->Delete(); } mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).as_ref(); } mitk::BoundingBox::Pointer mitk::BaseGeometry::CalculateBoundingBoxRelativeToTransform( const mitk::AffineTransform3D *transform) const { mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; if (transform != nullptr) { mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(GetCornerPoint(i))); } else { for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, GetCornerPoint(i)); } mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } const std::string mitk::BaseGeometry::GetTransformAsString(TransformType *transformType) { std::ostringstream out; out << '['; for (int i = 0; i < 3; ++i) { out << '['; for (int j = 0; j < 3; ++j) out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; out << ']'; } out << "]["; for (int i = 0; i < 3; ++i) out << transformType->GetOffset()[i] << ' '; out << "]\0"; return out.str(); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkmatrix); } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D & /*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::IndexToWorld(point, vec, vec). Use " "BaseGeometry::IndexToWorld(vec, vec) instead!"; // vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } 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(); } bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const { return m_GeometryTransform->IsIndexToWorldTransformNull(); } void mitk::BaseGeometry::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because // imageGeometries origins are pixel-center-based // ... and remove the offset, if you switch an imageGeometry back to a normal geometry // For more information please see the Geometry documentation page if (m_ImageGeometry == isAnImageGeometry) return; const BoundingBox::BoundsArrayType &boundsarray = this->GetBoundingBox()->GetBounds(); Point3D originIndex; FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); if (isAnImageGeometry == true) FillVector3D(originIndex, originIndex[0] + 0.5, originIndex[1] + 0.5, originIndex[2] + 0.5); else FillVector3D(originIndex, originIndex[0] - 0.5, originIndex[1] - 0.5, originIndex[2] - 0.5); Point3D originWorld; originWorld = GetIndexToWorldTransform()->TransformPoint(originIndex); // instead could as well call IndexToWorld(originIndex,originWorld); SetOrigin(originWorld); this->SetImageGeometry(isAnImageGeometry); } void mitk::BaseGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if (this->IsIndexToWorldTransformNull()) os << "nullptr" << std::endl; else { // from itk::MatrixOffsetTransformBase unsigned int i, j; os << std::endl; os << indent << "Matrix: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << this->GetIndexToWorldTransform()->GetOffset() << std::endl; os << indent << "Center: " << this->GetIndexToWorldTransform()->GetCenter() << std::endl; os << indent << "Translation: " << this->GetIndexToWorldTransform()->GetTranslation() << std::endl; auto inverse = mitk::AffineTransform3D::New(); if (this->GetIndexToWorldTransform()->GetInverse(inverse)) { os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << inverse->GetMatrix()[i][j] << " "; } os << std::endl; } } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << this->GetIndexToWorldTransform()->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if (this->IsBoundingBoxNull()) os << "nullptr" << std::endl; else { os << indent << "( "; for (unsigned int i = 0; i < 3; i++) { os << this->GetBoundingBox()->GetBounds()[2 * i] << "," << this->GetBoundingBox()->GetBounds()[2 * i + 1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << this->GetOrigin() << std::endl; os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl; os << indent << " Spacing: " << this->GetSpacing() << std::endl; } void mitk::BaseGeometry::Modified() const { if (!m_ModifiedLockFlag) Superclass::Modified(); else m_ModifiedCalledFlag = true; } mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() const { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::GeometryTransformHolder *mitk::BaseGeometry::GetGeometryTransformHolder() const { return m_GeometryTransform; } void mitk::BaseGeometry::MapAxesToOrientations(int axes[]) const { auto affineTransform = this->GetIndexToWorldTransform(); auto matrix = affineTransform->GetMatrix(); matrix.GetVnlMatrix().normalize_columns(); auto inverseMatrix = matrix.GetInverse(); bool mapped[3] = {false, false, false}; // We need to allow an epsilon difference to ignore rounding. const double eps = 0.0001; for (int orientation = 0; orientation < 3; ++orientation) { auto absX = std::abs(inverseMatrix[0][orientation]); auto absY = std::abs(inverseMatrix[1][orientation]); auto absZ = std::abs(inverseMatrix[2][orientation]); // First we check if there is a single maximum value. If there is, we found the axis // that corresponds to the given orientation. If there is no single maximum value, // we choose one from the the two or three axes that have the maximum value, but we // need to make sure that we do not map the same axis to different orientations. // Equal values are valid if the volume is rotated by exactly 45 degrees around one // axis. If the volume is rotated by 45 degrees around two axes, you will get single // maximum values at the same axes for two different orientations. In this case, // the axis is mapped to one of the orientations, and for the other orientation we // choose a different axis that has not been mapped yet, even if it is not a maximum. if (absX > absY + eps) { if (absX > absZ + eps) { // x is the greatest int axis = !mapped[0] ? 0 : !mapped[1] ? 1 : 2; axes[orientation] = axis; mapped[axis] = true; } else { // z is the greatest OR x and z are equal and greater than y int axis = !mapped[2] ? 2 : !mapped[0] ? 0 : 1; axes[orientation] = axis; mapped[axis] = true; } } else if (absY > absX + eps) { if (absY > absZ + eps) { // y is the greatest int axis = !mapped[1] ? 1 : !mapped[2] ? 2 : 0; axes[orientation] = axis; mapped[axis] = true; } else { // z is the greatest OR y and z are equal and greater than x int axis = !mapped[2] ? 2 : !mapped[1] ? 1 : 0; axes[orientation] = axis; mapped[axis] = true; } } else { if (absZ > absX + eps) { // z is the greatest int axis = !mapped[2] ? 2 : !mapped[0] ? 0 : 1; axes[orientation] = axis; mapped[axis] = true; } else { // x and y are equal and greater than z OR x and y and z are equal int axis = !mapped[0] ? 0 : !mapped[1] ? 1 : 2; axes[orientation] = axis; mapped[axis] = true; } } } assert(mapped[0] && mapped[1] && mapped[2]); } bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose) { bool result = true; BaseGeometry::BoundsArrayType rightBounds = rightHandSide.GetBounds(); BaseGeometry::BoundsArrayType leftBounds = leftHandSide.GetBounds(); BaseGeometry::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); for (BaseGeometry::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) { if ((!mitk::Equal(*itLeft, *itRight, eps))) { if (verbose) { MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; } result = false; } itLeft++; } return result; } bool mitk::Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare Origins if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << coordinateEps; } result = false; } // Compare Axis and Extents for (unsigned int i = 0; i < 3; ++i) { if (!mitk::Equal(leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), directionEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is " << leftHandSide.GetAxisVector(i) << " and tolerance is " << directionEps; } result = false; } if (!mitk::Equal(leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is " << leftHandSide.GetExtent(i) << " and tolerance is " << coordinateEps; } result = false; } } // Compare ImageGeometry Flag if (rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is " << leftHandSide.GetImageGeometry(); } result = false; } // Compare FrameOfReference ID if (rightHandSide.GetFrameOfReferenceID() != leftHandSide.GetFrameOfReferenceID()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetFrameOfReferenceID is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetFrameOfReferenceID() << " : leftHandSide is " << leftHandSide.GetFrameOfReferenceID(); } result = false; } // Compare BoundingBoxes if (!mitk::Equal(*leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), coordinateEps, verbose)) { result = false; } // Compare IndexToWorldTransform Matrix if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), directionEps, verbose)) { result = false; } return result; } bool mitk::Equal(const mitk::BaseGeometry& leftHandSide, const mitk::BaseGeometry& rightHandSide, ScalarType eps, bool verbose) { return Equal(leftHandSide, rightHandSide, eps, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose) { // Compare IndexToWorldTransform Matrix if (!mitk::MatrixEqualElementWise(leftHandSide.GetMatrix(), rightHandSide.GetMatrix(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is " << leftHandSide.GetMatrix() << " and tolerance is " << eps; } return false; } return true; } bool mitk::IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType coordinateEps, ScalarType directionEps, bool verbose) { bool result = true; // Compare spacings (must be equal) const auto testedSpacing = testGeo.GetSpacing(); if (!mitk::Equal(testedSpacing, referenceGeo.GetSpacing(), coordinateEps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "testedGeometry is " << setprecision(12) << testedSpacing << " : referenceGeometry is " << referenceGeo.GetSpacing() << " and tolerance is " << coordinateEps; } result = false; } // Compare ImageGeometry Flag (must be equal) if (referenceGeo.GetImageGeometry() != testGeo.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "referenceGeo is " << referenceGeo.GetImageGeometry() << " : testGeo is " << testGeo.GetImageGeometry(); } result = false; } // Compare IndexToWorldTransform Matrix (must be equal -> same axis directions) if (!Equal(*(testGeo.GetIndexToWorldTransform()), *(referenceGeo.GetIndexToWorldTransform()), directionEps, verbose)) { result = false; } //check if the geometry is within or equal to the bounds of reference geomentry. for (int i = 0; i<8; ++i) { auto testCorner = testGeo.GetCornerPoint(i); bool isInside = false; mitk::Point3D testCornerIndex; referenceGeo.WorldToIndex(testCorner, testCornerIndex); std::bitset<sizeof(int)> bs(i); //To regard the coordinateEps, we subtract or add it to the index elements //depending on whether it was constructed by a lower or an upper bound value //(see implementation of BaseGeometry::GetCorner()). if (bs.test(0)) { testCornerIndex[2] -= coordinateEps; } else { testCornerIndex[2] += coordinateEps; } if (bs.test(1)) { testCornerIndex[1] -= coordinateEps; } else { testCornerIndex[1] += coordinateEps; } if (bs.test(2)) { testCornerIndex[0] -= coordinateEps; } else { testCornerIndex[0] += coordinateEps; } isInside = referenceGeo.IsIndexInside(testCornerIndex); if (!isInside) { if (verbose) { MITK_INFO << "[( Geometry3D )] corner point is not inside. "; MITK_INFO << "referenceGeo is " << setprecision(12) << referenceGeo << " : tested corner is " << testGeo.GetCornerPoint(i); } result = false; } } // check grid of test geometry is on the grid of the reference geometry. This is important as the // boundingbox is only checked for containing the tested geometry, but if a corner (one is enough // as we know that axis and spacing are equal, due to equal transfor (see above)) of the tested geometry // is on the grid it is really a sub geometry (as they have the same spacing and axis). auto cornerOffset = testGeo.GetCornerPoint(0) - referenceGeo.GetCornerPoint(0); mitk::Vector3D cornerIndexOffset; referenceGeo.WorldToIndex(cornerOffset, cornerIndexOffset); for (unsigned int i = 0; i < 3; ++i) { auto pixelCountContinous = cornerIndexOffset[i]; auto pixelCount = std::round(pixelCountContinous); if (std::abs(pixelCount - pixelCountContinous) > coordinateEps) { if (verbose) { MITK_INFO << "[( Geometry3D )] Tested geometry is not on the grid of the reference geometry. "; MITK_INFO << "referenceGeo is " << setprecision(15) << referenceGeo << " : tested corner offset in pixels is " << pixelCountContinous << " for axis "<<i; } result = false; } } return result; } bool mitk::IsSubGeometry(const mitk::BaseGeometry& testGeo, const mitk::BaseGeometry& referenceGeo, ScalarType eps, bool verbose) { return IsSubGeometry(testGeo, referenceGeo, eps, eps, verbose); } diff --git a/Modules/Core/src/DataManagement/mitkClippingProperty.cpp b/Modules/Core/src/DataManagement/mitkClippingProperty.cpp index 5dc7014d1e..b3e5a677e4 100644 --- a/Modules/Core/src/DataManagement/mitkClippingProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkClippingProperty.cpp @@ -1,91 +1,110 @@ /*============================================================================ 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 "mitkClippingProperty.h" namespace mitk { ClippingProperty::ClippingProperty() : m_ClippingEnabled(false), m_Origin(0.0), m_Normal(0.0) {} ClippingProperty::ClippingProperty(const ClippingProperty &other) : BaseProperty(other), m_ClippingEnabled(other.m_ClippingEnabled), m_Origin(other.m_Origin), m_Normal(other.m_Normal) { } ClippingProperty::ClippingProperty(const Point3D &origin, const Vector3D &normal) : m_ClippingEnabled(true), m_Origin(origin), m_Normal(normal) { } bool ClippingProperty::GetClippingEnabled() const { return m_ClippingEnabled; } void ClippingProperty::SetClippingEnabled(bool enabled) { if (m_ClippingEnabled != enabled) { m_ClippingEnabled = enabled; this->Modified(); } } const Point3D &ClippingProperty::GetOrigin() const { return m_Origin; } void ClippingProperty::SetOrigin(const Point3D &origin) { if (m_Origin != origin) { m_Origin = origin; this->Modified(); } } const Vector3D &ClippingProperty::GetNormal() const { return m_Normal; } void ClippingProperty::SetNormal(const Vector3D &normal) { if (m_Normal != normal) { m_Normal = normal; this->Modified(); } } bool ClippingProperty::IsEqual(const BaseProperty &property) const { return ((this->m_ClippingEnabled == static_cast<const Self &>(property).m_ClippingEnabled) && (this->m_Origin == static_cast<const Self &>(property).m_Origin) && (this->m_Normal == static_cast<const Self &>(property).m_Normal)); } bool ClippingProperty::Assign(const BaseProperty &property) { this->m_ClippingEnabled = static_cast<const Self &>(property).m_ClippingEnabled; this->m_Origin = static_cast<const Self &>(property).m_Origin; this->m_Normal = static_cast<const Self &>(property).m_Normal; return true; } std::string ClippingProperty::GetValueAsString() const { std::stringstream myStr; myStr << this->GetClippingEnabled() << this->GetOrigin() << this->GetNormal(); 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<bool>()); + this->SetOrigin(j["Origin"].get<Point3D>()); + this->SetNormal(j["Normal"].get<Vector3D>()); + + return true; + } + itk::LightObject::Pointer ClippingProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } } // namespace diff --git a/Modules/Core/src/DataManagement/mitkColorProperty.cpp b/Modules/Core/src/DataManagement/mitkColorProperty.cpp index 9288a7343e..25d48f86a3 100644 --- a/Modules/Core/src/DataManagement/mitkColorProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkColorProperty.cpp @@ -1,90 +1,103 @@ /*============================================================================ 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 "mitkColorProperty.h" #include <sstream> +#include <nlohmann/json.hpp> mitk::ColorProperty::ColorProperty() : m_Color(0.0f) { } mitk::ColorProperty::ColorProperty(const mitk::ColorProperty &other) : BaseProperty(other), m_Color(other.m_Color) { } mitk::ColorProperty::ColorProperty(const float color[3]) : m_Color(color) { } mitk::ColorProperty::ColorProperty(const float red, const float green, const float blue) { m_Color.Set(red, green, blue); } mitk::ColorProperty::ColorProperty(const mitk::Color &color) : m_Color(color) { } bool mitk::ColorProperty::IsEqual(const BaseProperty &property) const { return this->m_Color == static_cast<const Self &>(property).m_Color; } bool mitk::ColorProperty::Assign(const BaseProperty &property) { this->m_Color = static_cast<const Self &>(property).m_Color; return true; } const mitk::Color &mitk::ColorProperty::GetColor() const { return m_Color; } void mitk::ColorProperty::SetColor(const mitk::Color &color) { if (m_Color != color) { m_Color = color; Modified(); } } void mitk::ColorProperty::SetValue(const mitk::Color &color) { SetColor(color); } void mitk::ColorProperty::SetColor(float red, float green, float blue) { float tmp[3] = {red, green, blue}; SetColor(mitk::Color(tmp)); } std::string mitk::ColorProperty::GetValueAsString() const { std::stringstream myStr; myStr.imbue(std::locale::classic()); myStr << GetValue(); return myStr.str(); } const mitk::Color &mitk::ColorProperty::GetValue() const { return GetColor(); } +bool mitk::ColorProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetColor(); + return true; +} + +bool mitk::ColorProperty::FromJSON(const nlohmann::json& j) +{ + this->SetColor(j.get<Color>()); + return true; +} + itk::LightObject::Pointer mitk::ColorProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp index e6e3d27852..2019bf5375 100644 --- a/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkEnumerationProperty.cpp @@ -1,166 +1,183 @@ /*============================================================================ 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 <mitkEnumerationProperty.h> #include <algorithm> +#include <nlohmann/json.hpp> mitk::EnumerationProperty::EnumerationProperty() : m_CurrentValue(0) { } mitk::EnumerationProperty::EnumerationProperty(const EnumerationProperty &other) : BaseProperty(other), m_CurrentValue(other.m_CurrentValue), m_IdMap(other.m_IdMap), m_NameMap(other.m_NameMap) { } bool mitk::EnumerationProperty::AddEnum(const std::string &name, const IdType &id) { if (false == this->IsValidEnumerationValue(id) && false == this->IsValidEnumerationValue(name)) { this->GetEnumIds().insert(std::make_pair(id, name)); this->GetEnumStrings().insert(std::make_pair(name, id)); return true; } return false; } bool mitk::EnumerationProperty::SetValue(const std::string &name) { if (this->IsValidEnumerationValue(name)) { m_CurrentValue = this->GetEnumId(name); this->Modified(); return true; } return false; } bool mitk::EnumerationProperty::SetValue(const IdType &id) { if (this->IsValidEnumerationValue(id)) { m_CurrentValue = id; this->Modified(); return true; } return false; } mitk::EnumerationProperty::IdType mitk::EnumerationProperty::GetValueAsId() const { return m_CurrentValue; } std::string mitk::EnumerationProperty::GetValueAsString() const { return this->GetEnumString(m_CurrentValue); } void mitk::EnumerationProperty::Clear() { this->GetEnumIds().clear(); this->GetEnumStrings().clear(); m_CurrentValue = 0; } mitk::EnumerationProperty::EnumIdsContainerType::size_type mitk::EnumerationProperty::Size() const { return this->GetEnumIds().size(); } mitk::EnumerationProperty::EnumConstIterator mitk::EnumerationProperty::Begin() const { return this->GetEnumIds().cbegin(); } mitk::EnumerationProperty::EnumConstIterator mitk::EnumerationProperty::End() const { return this->GetEnumIds().cend(); } std::string mitk::EnumerationProperty::GetEnumString(const IdType &id) const { return this->IsValidEnumerationValue(id) ? this->GetEnumIds().find(id)->second : std::string("invalid enum id or enums empty"); } mitk::EnumerationProperty::IdType mitk::EnumerationProperty::GetEnumId(const std::string &name) const { return this->IsValidEnumerationValue(name) ? this->GetEnumStrings().find(name)->second : 0; } bool mitk::EnumerationProperty::IsEqual(const BaseProperty &property) const { const auto &other = static_cast<const Self &>(property); return this->Size() == other.Size() && this->GetValueAsId() == other.GetValueAsId() && std::equal(this->Begin(), this->End(), other.Begin()); } bool mitk::EnumerationProperty::Assign(const BaseProperty &property) { const auto &other = static_cast<const Self &>(property); this->GetEnumIds() = other.GetEnumIds(); this->GetEnumStrings() = other.GetEnumStrings(); m_CurrentValue = other.m_CurrentValue; return true; } bool mitk::EnumerationProperty::IsValidEnumerationValue(const IdType &id) const { return this->GetEnumIds().end() != this->GetEnumIds().find(id); } bool mitk::EnumerationProperty::IsValidEnumerationValue(const std::string &name) const { return this->GetEnumStrings().end() != this->GetEnumStrings().find(name); } mitk::EnumerationProperty::EnumIdsContainerType & mitk::EnumerationProperty::GetEnumIds() { return m_IdMap; } const mitk::EnumerationProperty::EnumIdsContainerType & mitk::EnumerationProperty::GetEnumIds() const { return m_IdMap; } mitk::EnumerationProperty::EnumStringsContainerType & mitk::EnumerationProperty::GetEnumStrings() { return m_NameMap; } const mitk::EnumerationProperty::EnumStringsContainerType & mitk::EnumerationProperty::GetEnumStrings() const { return m_NameMap; } + +bool mitk::EnumerationProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetValueAsString(); + return true; +} + +bool mitk::EnumerationProperty::FromJSON(const nlohmann::json& j) +{ + auto name = j.get<std::string>(); + + if (!this->SetValue(name)) + mitkThrow() << '"' << name << "\" is not a valid enumeration value for " << this->GetNameOfClass() << '!'; + + return true; +} diff --git a/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp b/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp index 2fc2aa5e05..272bbec8ff 100644 --- a/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkGroupTagProperty.cpp @@ -1,39 +1,51 @@ /*============================================================================ 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 "mitkGroupTagProperty.h" +#include <nlohmann/json.hpp> mitk::GroupTagProperty::GroupTagProperty() : mitk::BaseProperty() { } mitk::GroupTagProperty::GroupTagProperty(const GroupTagProperty &other) : mitk::BaseProperty(other) { } bool mitk::GroupTagProperty::IsEqual(const BaseProperty & /*property*/) const { // if other property is also a GroupTagProperty, then it is equal to us, because tags have no value themselves return true; } 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)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkLine.cpp b/Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp similarity index 78% rename from Modules/Core/src/DataManagement/mitkLine.cpp rename to Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp index 4d6ae904bc..bb3e4b2c12 100644 --- a/Modules/Core/src/DataManagement/mitkLine.cpp +++ b/Modules/Core/src/DataManagement/mitkIPropertyDeserialization.cpp @@ -1,13 +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 "mitkLine.h" +#include "mitkIPropertyDeserialization.h" + +mitk::IPropertyDeserialization::~IPropertyDeserialization() +{ +} diff --git a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp index c3d8841919..be8a030d6a 100644 --- a/Modules/Core/src/DataManagement/mitkLevelWindow.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindow.cpp @@ -1,510 +1,548 @@ /*============================================================================ 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 "mitkLevelWindow.h" #include "mitkImage.h" #include "mitkImageSliceSelector.h" #include "mitkImageStatisticsHolder.h" #include <algorithm> +#include <nlohmann/json.hpp> void mitk::LevelWindow::EnsureConsistency() { // Check if total range is ok { if (m_RangeMin > m_RangeMax) std::swap(m_RangeMin, m_RangeMax); if (m_RangeMin == m_RangeMax) m_RangeMin = m_RangeMax - 1; } // Check if current window is ok { if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound <= m_RangeMin) m_LowerWindowBound = m_RangeMin; if (m_UpperWindowBound <= m_RangeMin) m_UpperWindowBound = m_RangeMin + 1; if (m_LowerWindowBound >= m_RangeMax) m_LowerWindowBound = m_RangeMax - 1; if (m_UpperWindowBound >= m_RangeMax) m_UpperWindowBound = m_RangeMax; if (m_LowerWindowBound == m_UpperWindowBound) { m_UpperWindowBound += 0.5; m_LowerWindowBound -= 0.5; m_UpperWindowBound = std::min(m_UpperWindowBound, m_RangeMax); m_LowerWindowBound = std::max(m_LowerWindowBound, m_RangeMin); } } } mitk::LevelWindow::LevelWindow(mitk::ScalarType level, mitk::ScalarType window) : m_LowerWindowBound(level - window / 2.0), m_UpperWindowBound(level + window / 2.0), m_RangeMin(-2048.0), m_RangeMax(4096.0), m_DefaultLowerBound(-2048.0), m_DefaultUpperBound(4096.0), m_IsFloatingImage(false), m_Fixed(false) { SetDefaultLevelWindow(level, window); SetLevelWindow(level, window, true); } mitk::LevelWindow::LevelWindow(const mitk::LevelWindow &levWin) : m_LowerWindowBound(levWin.GetLowerWindowBound()), m_UpperWindowBound(levWin.GetUpperWindowBound()), m_RangeMin(levWin.GetRangeMin()), m_RangeMax(levWin.GetRangeMax()), m_DefaultLowerBound(levWin.GetDefaultLowerBound()), m_DefaultUpperBound(levWin.GetDefaultUpperBound()), m_IsFloatingImage(levWin.IsFloatingValues()), m_Fixed(levWin.GetFixed()) { } mitk::LevelWindow::~LevelWindow() { } mitk::ScalarType mitk::LevelWindow::GetLevel() const { return (m_UpperWindowBound - m_LowerWindowBound) / 2.0 + m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetWindow() const { return (m_UpperWindowBound - m_LowerWindowBound); } mitk::ScalarType mitk::LevelWindow::GetDefaultLevel() const { return ((m_DefaultUpperBound + m_DefaultLowerBound) / 2.0); } mitk::ScalarType mitk::LevelWindow::GetDefaultWindow() const { return ((m_DefaultUpperBound - m_DefaultLowerBound)); } void mitk::LevelWindow::ResetDefaultLevelWindow() { SetLevelWindow(GetDefaultLevel(), GetDefaultWindow()); } mitk::ScalarType mitk::LevelWindow::GetLowerWindowBound() const { return m_LowerWindowBound; } mitk::ScalarType mitk::LevelWindow::GetUpperWindowBound() const { return m_UpperWindowBound; } void mitk::LevelWindow::SetDefaultLevelWindow(mitk::ScalarType level, mitk::ScalarType window) { SetDefaultBoundaries((level - (window / 2.0)), (level + (window / 2.0))); } void mitk::LevelWindow::SetLevelWindow(mitk::ScalarType level, mitk::ScalarType window, bool expandRangesIfNecessary) { SetWindowBounds((level - (window / 2.0)), (level + (window / 2.0)), expandRangesIfNecessary); } void mitk::LevelWindow::SetWindowBounds(mitk::ScalarType lowerBound, mitk::ScalarType upperBound, bool expandRangesIfNecessary) { if (IsFixed()) return; upperBound = std::clamp(upperBound, -1e300, 1e300); lowerBound = std::clamp(lowerBound, -1e300, 1e300); m_LowerWindowBound = lowerBound; m_UpperWindowBound = upperBound; if (expandRangesIfNecessary) { /* if caller is sure he wants exactly that level/window, we make sure the limits match */ if (m_LowerWindowBound > m_UpperWindowBound) std::swap(m_LowerWindowBound, m_UpperWindowBound); if (m_LowerWindowBound < m_RangeMin) { m_RangeMin = m_LowerWindowBound; } if (m_UpperWindowBound > m_RangeMax) { m_RangeMax = m_UpperWindowBound; } } EnsureConsistency(); } void mitk::LevelWindow::SetRangeMinMax(mitk::ScalarType min, mitk::ScalarType max) { if (IsFixed()) return; m_RangeMin = min; m_RangeMax = max; EnsureConsistency(); } void mitk::LevelWindow::SetDefaultBoundaries(mitk::ScalarType low, mitk::ScalarType up) { if (IsFixed()) return; m_DefaultLowerBound = low; m_DefaultUpperBound = up; // Check if default window is ok { if (m_DefaultLowerBound > m_DefaultUpperBound) std::swap(m_DefaultLowerBound, m_DefaultUpperBound); if (m_DefaultLowerBound == m_DefaultUpperBound) m_DefaultLowerBound--; } EnsureConsistency(); } void mitk::LevelWindow::SetToMaxWindowSize() { SetWindowBounds(m_RangeMin, m_RangeMax); } mitk::ScalarType mitk::LevelWindow::GetRangeMin() const { return m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetRangeMax() const { return m_RangeMax; } mitk::ScalarType mitk::LevelWindow::GetRange() const { return m_RangeMax - m_RangeMin; } mitk::ScalarType mitk::LevelWindow::GetDefaultUpperBound() const { return m_DefaultUpperBound; } mitk::ScalarType mitk::LevelWindow::GetDefaultLowerBound() const { return m_DefaultLowerBound; } void mitk::LevelWindow::ResetDefaultRangeMinMax() { SetRangeMinMax(m_DefaultLowerBound, m_DefaultUpperBound); } /*! This method initializes a mitk::LevelWindow from an mitk::Image. The algorithm is as follows: Default to taking the central image slice for quick analysis. Compute the smallest (minValue), second smallest (min2ndValue), second largest (max2ndValue), and largest (maxValue) data value by traversing the pixel values only once. In the same scan it also computes the count of minValue values and maxValue values. After that a basic histogram with specific information about the extremes is complete. If minValue == maxValue, the center slice is uniform and the above scan is repeated for the complete image, not just one slice Next, special cases of images with only 1, 2 or 3 distinct data values have hand assigned level window ranges. Next the level window is set relative to the inner range IR = lengthOf([min2ndValue, max2ndValue]) For count(minValue) > 20% the smallest values are frequent and should be distinct from the min2ndValue and larger values (minValue may be std:min, may signify something special) hence the lower end of the level window is set to min2ndValue - 0.5 * IR For count(minValue) <= 20% the smallest values are not so important and can blend with the next ones => min(level window) = min2ndValue And analog for max(level window): count(max2ndValue) > 20%: max(level window) = max2ndValue + 0.5 * IR count(max2ndValue) < 20%: max(level window) = max2ndValue In both 20%+ cases the level window bounds are clamped to the [minValue, maxValue] range In consequence the level window maximizes contrast with minimal amount of computation and does do useful things if the data contains std::min or std:max values or has only 1 or 2 or 3 data values. */ void mitk::LevelWindow::SetAuto(const mitk::Image *image, bool /*tryPicTags*/, bool guessByCentralSlice, unsigned selectedComponent) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; if (itk::IOComponentEnum::FLOAT == image->GetPixelType().GetComponentType() || itk::IOComponentEnum::DOUBLE == image->GetPixelType().GetComponentType()) { m_IsFloatingImage = true; } else { m_IsFloatingImage = false; } const mitk::Image *wholeImage = image; ScalarType minValue = 0.0; ScalarType maxValue = 0.0; ScalarType min2ndValue = 0.0; ScalarType max2ndValue = 0.0; mitk::ImageSliceSelector::Pointer sliceSelector = mitk::ImageSliceSelector::New(); if (guessByCentralSlice) { sliceSelector->SetInput(image); sliceSelector->SetSliceNr(image->GetDimension(2) / 2); sliceSelector->SetTimeNr(image->GetDimension(3) / 2); sliceSelector->SetChannelNr(image->GetDimension(4) / 2); sliceSelector->Update(); image = sliceSelector->GetOutput(); if (image == nullptr || !image->IsInitialized()) return; minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); if (minValue == maxValue) { // guessByCentralSlice seems to have failed, lets look at all data image = wholeImage; minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(); } } else { const_cast<Image *>(image)->Update(); minValue = image->GetStatistics()->GetScalarValueMin(0, selectedComponent); maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); min2ndValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); max2ndValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); for (unsigned int i = 1; i < image->GetDimension(3); ++i) { ScalarType minValueTemp = image->GetStatistics()->GetScalarValueMin(i, selectedComponent); if (minValue > minValueTemp) minValue = minValueTemp; ScalarType maxValueTemp = image->GetStatistics()->GetScalarValueMaxNoRecompute(i); if (maxValue < maxValueTemp) maxValue = maxValueTemp; ScalarType min2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(i); if (min2ndValue > min2ndValueTemp) min2ndValue = min2ndValueTemp; ScalarType max2ndValueTemp = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(i); if (max2ndValue > max2ndValueTemp) max2ndValue = max2ndValueTemp; } } // Fix for bug# 344 Level Window wird bei Eris Cut bildern nicht richtig gesetzt if (image->GetPixelType().GetPixelType() == itk::IOPixelEnum::SCALAR && image->GetPixelType().GetComponentType() == itk::IOComponentEnum::INT && image->GetPixelType().GetBpe() >= 8) { // the windows compiler complains about ambiguous 'pow' call, therefore static casting to (double, int) if (minValue == -(pow((double)2.0, static_cast<int>(image->GetPixelType().GetBpe() / 2)))) { minValue = min2ndValue; } } // End fix //// uniform image if (minValue == maxValue) { minValue = maxValue - 1; } else { // Due to bug #8690 level window now is no longer of fixed range by default but the range adapts according to // levelwindow interaction // This is done because the range should be a little bit larger from the beginning so that the scale doesn't start // to resize right from the beginning double additionalRange = 0.15 * (maxValue - minValue); minValue -= additionalRange; maxValue += additionalRange; } if (!std::isfinite(minValue)) { minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); } if (!std::isfinite(maxValue)) { maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); size_t numPixelsInDataset = image->GetDimensions()[0]; for (decltype(image->GetDimension()) k = 1; k < image->GetDimension(); ++k) numPixelsInDataset *= image->GetDimensions()[k]; const auto minCount = image->GetStatistics()->GetCountOfMinValuedVoxelsNoRecompute(); const auto maxCount = image->GetStatistics()->GetCountOfMaxValuedVoxelsNoRecompute(); const auto minCountFraction = minCount / static_cast<ScalarType>(numPixelsInDataset); const auto maxCountFraction = maxCount / static_cast<ScalarType>(numPixelsInDataset); //// binary image if (min2ndValue == maxValue) { // noop; full range is fine } //// triple value image, put middle value in center of gray level ramp else if (min2ndValue == max2ndValue) { ScalarType minDelta = std::min(min2ndValue - minValue, maxValue - min2ndValue); minValue = min2ndValue - minDelta; maxValue = min2ndValue + minDelta; } // now we can assume more than three distict scalar values else { ScalarType innerRange = max2ndValue - min2ndValue; if (minCountFraction > 0.2) //// lots of min values -> make different from rest, but not miles away { ScalarType halfInnerRangeGapMinValue = min2ndValue - innerRange / 2.0; minValue = std::max(minValue, halfInnerRangeGapMinValue); } else //// few min values -> focus on innerRange { minValue = min2ndValue; } if (maxCountFraction > 0.2) //// lots of max values -> make different from rest { ScalarType halfInnerRangeGapMaxValue = max2ndValue + innerRange / 2.0; maxValue = std::min(maxValue, halfInnerRangeGapMaxValue); } else //// few max values -> focus on innerRange { maxValue = max2ndValue; } } SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetToImageRange(const mitk::Image *image) { if (IsFixed()) return; if (image == nullptr || !image->IsInitialized()) return; ScalarType minValue = image->GetStatistics()->GetScalarValueMin(0); if (!std::isfinite(minValue)) { minValue = image->GetStatistics()->GetScalarValue2ndMinNoRecompute(0); } ScalarType maxValue = image->GetStatistics()->GetScalarValueMaxNoRecompute(0); if (!std::isfinite(maxValue)) { maxValue = image->GetStatistics()->GetScalarValue2ndMaxNoRecompute(0); } SetRangeMinMax(minValue, maxValue); SetDefaultBoundaries(minValue, maxValue); SetWindowBounds(minValue, maxValue); SetDefaultLevelWindow((maxValue - minValue) / 2 + minValue, maxValue - minValue); } void mitk::LevelWindow::SetFixed(bool fixed) { m_Fixed = fixed; } bool mitk::LevelWindow::GetFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFixed() const { return m_Fixed; } bool mitk::LevelWindow::IsFloatingValues() const { return m_IsFloatingImage; } void mitk::LevelWindow::SetFloatingValues(bool value) { m_IsFloatingImage = value; } bool mitk::LevelWindow::operator==(const mitk::LevelWindow &levWin) const { return mitk::Equal(this->m_RangeMin, levWin.m_RangeMin, mitk::sqrteps) && mitk::Equal(this->m_RangeMax, levWin.m_RangeMax, mitk::sqrteps) && mitk::Equal(this->m_DefaultLowerBound, levWin.m_DefaultLowerBound, mitk::sqrteps) && mitk::Equal(this->m_DefaultUpperBound, levWin.m_DefaultUpperBound, mitk::sqrteps) && mitk::Equal(this->m_LowerWindowBound, levWin.m_LowerWindowBound, mitk::sqrteps) && mitk::Equal(this->m_UpperWindowBound, levWin.m_UpperWindowBound, mitk::sqrteps) && m_Fixed == levWin.IsFixed() && m_IsFloatingImage == levWin.IsFloatingValues(); } bool mitk::LevelWindow::operator!=(const mitk::LevelWindow &levWin) const { return !((*this) == levWin); } mitk::LevelWindow &mitk::LevelWindow::operator=(const mitk::LevelWindow &levWin) { if (this == &levWin) { return *this; } else { m_RangeMin = levWin.GetRangeMin(); m_RangeMax = levWin.GetRangeMax(); m_LowerWindowBound = levWin.GetLowerWindowBound(); m_UpperWindowBound = levWin.GetUpperWindowBound(); m_DefaultLowerBound = levWin.GetDefaultLowerBound(); m_DefaultUpperBound = levWin.GetDefaultUpperBound(); m_Fixed = levWin.GetFixed(); m_IsFloatingImage = levWin.IsFloatingValues(); 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<ScalarType>(), + j["CurrentRange"]["Max"].get<ScalarType>()); + + lw.SetDefaultLevelWindow( + j["DefaultSettings"]["Level"].get<ScalarType>(), + j["DefaultSettings"]["Window"].get<ScalarType>()); + + lw.SetLevelWindow( + j["CurrentSettings"]["Level"].get<ScalarType>(), + j["CurrentSettings"]["Window"].get<ScalarType>()); + + lw.SetFixed(j["Fixed"].get<bool>()); + lw.SetFloatingValues(j["IsFloatingImage"].get<bool>()); + } +} diff --git a/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp b/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp index 5376d63183..2f82af0ba7 100755 --- a/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkLevelWindowProperty.cpp @@ -1,80 +1,92 @@ /*============================================================================ 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 "mitkLevelWindowProperty.h" mitk::LevelWindowProperty::LevelWindowProperty() { } mitk::LevelWindowProperty::LevelWindowProperty(const mitk::LevelWindowProperty &other) : BaseProperty(other), m_LevWin(other.m_LevWin) { } mitk::LevelWindowProperty::LevelWindowProperty(const mitk::LevelWindow &levWin) { SetLevelWindow(levWin); } mitk::LevelWindowProperty::~LevelWindowProperty() { } bool mitk::LevelWindowProperty::IsEqual(const BaseProperty &property) const { return this->m_LevWin == static_cast<const Self &>(property).m_LevWin; } bool mitk::LevelWindowProperty::Assign(const BaseProperty &property) { this->m_LevWin = static_cast<const Self &>(property).m_LevWin; return true; } const mitk::LevelWindow &mitk::LevelWindowProperty::GetLevelWindow() const { return m_LevWin; } const mitk::LevelWindow &mitk::LevelWindowProperty::GetValue() const { return GetLevelWindow(); } void mitk::LevelWindowProperty::SetLevelWindow(const mitk::LevelWindow &levWin) { if (m_LevWin != levWin) { m_LevWin = levWin; Modified(); } } void mitk::LevelWindowProperty::SetValue(const ValueType &levWin) { SetLevelWindow(levWin); } std::string mitk::LevelWindowProperty::GetValueAsString() const { std::stringstream myStr; myStr << "L:" << m_LevWin.GetLevel() << " W:" << m_LevWin.GetWindow(); 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<LevelWindow>()); + return true; +} + itk::LightObject::Pointer mitk::LevelWindowProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp index c079e4f0c4..43bf40cf23 100644 --- a/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkLookupTableProperty.cpp @@ -1,73 +1,153 @@ /*============================================================================ 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 "mitkLookupTableProperty.h" +#include <nlohmann/json.hpp> mitk::LookupTableProperty::LookupTableProperty() { mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); this->SetLookupTable(lut); } mitk::LookupTableProperty::LookupTableProperty(const LookupTableProperty &other) : mitk::BaseProperty(other), m_LookupTable(other.m_LookupTable) { } mitk::LookupTableProperty::LookupTableProperty(const mitk::LookupTable::Pointer lut) { this->SetLookupTable(lut); } bool mitk::LookupTableProperty::IsEqual(const BaseProperty &property) const { return *(this->m_LookupTable) == *(static_cast<const Self &>(property).m_LookupTable); } bool mitk::LookupTableProperty::Assign(const BaseProperty &property) { this->m_LookupTable = static_cast<const Self &>(property).m_LookupTable; return true; } std::string mitk::LookupTableProperty::GetValueAsString() const { std::stringstream ss; ss << m_LookupTable; return ss.str(); } mitk::LookupTableProperty::ValueType mitk::LookupTableProperty::GetValue() const { return m_LookupTable; } void mitk::LookupTableProperty::SetLookupTable(const mitk::LookupTable::Pointer aLookupTable) { if ((m_LookupTable != aLookupTable) || (*m_LookupTable != *aLookupTable)) { m_LookupTable = aLookupTable; Modified(); } } void mitk::LookupTableProperty::SetValue(const ValueType &value) { SetLookupTable(value); } +bool 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(); + j["NumberOfColors"] = static_cast<int>(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; +} + +bool mitk::LookupTableProperty::FromJSON(const nlohmann::json& j) +{ + auto lut = vtkSmartPointer<vtkLookupTable>::New(); + + lut->SetNumberOfTableValues(j["NumberOfColors"].get<int>()); + lut->SetScale(j["Scale"].get<int>()); + lut->SetScale(j["Ramp"].get<int>()); + + std::array<double, 2> 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<double, 4> 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); + + return true; +} + itk::LightObject::Pointer mitk::LookupTableProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp b/Modules/Core/src/DataManagement/mitkPropertyDeserialization.cpp new file mode 100644 index 0000000000..251f2b9ff6 --- /dev/null +++ b/Modules/Core/src/DataManagement/mitkPropertyDeserialization.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 <mitkPropertyDeserialization.h> +#include <mitkBaseProperty.h> + +mitk::PropertyDeserialization::PropertyDeserialization() +{ +} + +mitk::PropertyDeserialization::~PropertyDeserialization() +{ +} + +mitk::BaseProperty::Pointer mitk::PropertyDeserialization::CreateInstance(const std::string& className) +{ + auto it = m_Map.find(className); + + if (it != m_Map.end()) + return static_cast<BaseProperty*>(it->second->CreateAnother().GetPointer()); + + return nullptr; +} + +void mitk::PropertyDeserialization::InternalRegisterProperty(const BaseProperty* property) +{ + m_Map[property->GetNameOfClass()] = property; +} diff --git a/Modules/Core/src/DataManagement/mitkPropertyList.cpp b/Modules/Core/src/DataManagement/mitkPropertyList.cpp index d15eac3f6e..88d5d7e34c 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyList.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyList.cpp @@ -1,376 +1,422 @@ /*============================================================================ 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 "mitkPropertyList.h" - -#include "mitkNumericTypes.h" -#include "mitkProperties.h" -#include "mitkStringProperty.h" +#include <mitkPropertyList.h> +#include <mitkCoreServices.h> +#include <mitkIPropertyDeserialization.h> +#include <mitkProperties.h> +#include <mitkStringProperty.h> mitk::BaseProperty::ConstPointer mitk::PropertyList::GetConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) const { PropertyMap::const_iterator it; it = m_Properties.find(propertyKey); if (it != m_Properties.cend()) return it->second.GetPointer(); else return nullptr; }; std::vector<std::string> mitk::PropertyList::GetPropertyKeys(const std::string &contextName, bool includeDefaultContext) const { std::vector<std::string> propertyKeys; if (contextName.empty() || includeDefaultContext) { for (const auto &property : this->m_Properties) propertyKeys.push_back(property.first); } return propertyKeys; }; std::vector<std::string> mitk::PropertyList::GetPropertyContextNames() const { return std::vector<std::string>(); }; mitk::BaseProperty *mitk::PropertyList::GetProperty(const std::string &propertyKey) const { PropertyMap::const_iterator it; it = m_Properties.find(propertyKey); if (it != m_Properties.cend()) return it->second; else return nullptr; } mitk::BaseProperty * mitk::PropertyList::GetNonConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { return this->GetProperty(propertyKey); } void mitk::PropertyList::SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { if (propertyKey.empty()) mitkThrow() << "Property key is empty."; if (!property) return; // make sure that BaseProperty*, which may have just been created and never been // assigned to a SmartPointer, is registered/unregistered properly. If we do not // do that, it will a) not deleted in case it is identical to the old one or // b) possibly deleted when temporarily added to a smartpointer somewhere below. BaseProperty::Pointer tmpSmartPointerToProperty = property; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { // yes // is the property contained in the list identical to the new one? if (it->second->operator==(*property)) { // yes? do nothing and return. return; } if (it->second->AssignProperty(*property)) { // The assignment was successful this->Modified(); } else { MITK_ERROR << "In " __FILE__ ", l." << __LINE__ << ": Trying to set existing property " << it->first << " of type " << it->second->GetNameOfClass() << " to a property with different type " << property->GetNameOfClass() << "." << " Use ReplaceProperty() instead." << std::endl; } return; } // no? add it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); this->Modified(); } void mitk::PropertyList::ReplaceProperty(const std::string &propertyKey, BaseProperty *property) { if (!property) return; auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); } // no? add/replace it. m_Properties.insert(PropertyMap::value_type(propertyKey, property)); Modified(); } void mitk::PropertyList::RemoveProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) { auto it(m_Properties.find(propertyKey)); // Is a property with key @a propertyKey contained in the list? if (it != m_Properties.cend()) { it->second = nullptr; m_Properties.erase(it); Modified(); } } mitk::PropertyList::PropertyList() { } mitk::PropertyList::PropertyList(const mitk::PropertyList &other) : itk::Object() { for (auto i = other.m_Properties.cbegin(); i != other.m_Properties.cend(); ++i) { m_Properties.insert(std::make_pair(i->first, i->second->Clone())); } } mitk::PropertyList::~PropertyList() { Clear(); } /** * Consider the list as changed when any of the properties has changed recently. */ itk::ModifiedTimeType mitk::PropertyList::GetMTime() const { for (auto it = m_Properties.cbegin(); it != m_Properties.cend(); ++it) { if (it->second.IsNull()) { itkWarningMacro(<< "Property '" << it->first << "' contains nothing (nullptr)."); continue; } if (Superclass::GetMTime() < it->second->GetMTime()) { Modified(); break; } } return Superclass::GetMTime(); } bool mitk::PropertyList::DeleteProperty(const std::string &propertyKey) { auto it = m_Properties.find(propertyKey); if (it != m_Properties.end()) { it->second = nullptr; m_Properties.erase(it); Modified(); return true; } return false; } void mitk::PropertyList::Clear() { auto it = m_Properties.begin(), end = m_Properties.end(); while (it != end) { it->second = nullptr; ++it; } m_Properties.clear(); } itk::LightObject::Pointer mitk::PropertyList::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } void mitk::PropertyList::ConcatenatePropertyList(PropertyList *pList, bool replace) { if (pList) { const PropertyMap *propertyMap = pList->GetMap(); for (auto iter = propertyMap->cbegin(); // m_PropertyList is created in the constructor, so we don't check it here iter != propertyMap->cend(); ++iter) { const std::string key = iter->first; BaseProperty *value = iter->second; if (replace) { ReplaceProperty(key.c_str(), value); } else { SetProperty(key.c_str(), value); } } } } bool mitk::PropertyList::GetBoolProperty(const char *propertyKey, bool &boolValue) const { BoolProperty *gp = dynamic_cast<BoolProperty *>(GetProperty(propertyKey)); if (gp != nullptr) { boolValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue<bool>(propertyKey, boolValue); } bool mitk::PropertyList::GetIntProperty(const char *propertyKey, int &intValue) const { IntProperty *gp = dynamic_cast<IntProperty *>(GetProperty(propertyKey)); if (gp != nullptr) { intValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue<int>(propertyKey, intValue); } bool mitk::PropertyList::GetFloatProperty(const char *propertyKey, float &floatValue) const { FloatProperty *gp = dynamic_cast<FloatProperty *>(GetProperty(propertyKey)); if (gp != nullptr) { floatValue = gp->GetValue(); return true; } return false; // Templated Method does not work on Macs // return GetPropertyValue<float>(propertyKey, floatValue); } bool mitk::PropertyList::GetStringProperty(const char *propertyKey, std::string &stringValue) const { StringProperty *sp = dynamic_cast<StringProperty *>(GetProperty(propertyKey)); if (sp != nullptr) { stringValue = sp->GetValue(); return true; } return false; } void mitk::PropertyList::SetIntProperty(const char *propertyKey, int intValue) { SetProperty(propertyKey, mitk::IntProperty::New(intValue)); } void mitk::PropertyList::SetBoolProperty(const char *propertyKey, bool boolValue) { SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); } void mitk::PropertyList::SetFloatProperty(const char *propertyKey, float floatValue) { SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); } void mitk::PropertyList::SetStringProperty(const char *propertyKey, const char *stringValue) { SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); } void mitk::PropertyList::Set(const char *propertyKey, bool boolValue) { this->SetBoolProperty(propertyKey, boolValue); } void mitk::PropertyList::Set(const char *propertyKey, int intValue) { this->SetIntProperty(propertyKey, intValue); } void mitk::PropertyList::Set(const char *propertyKey, float floatValue) { this->SetFloatProperty(propertyKey, floatValue); } void mitk::PropertyList::Set(const char *propertyKey, double doubleValue) { this->SetDoubleProperty(propertyKey, doubleValue); } void mitk::PropertyList::Set(const char *propertyKey, const char *stringValue) { this->SetStringProperty(propertyKey, stringValue); } void mitk::PropertyList::Set(const char *propertyKey, const std::string &stringValue) { this->SetStringProperty(propertyKey, stringValue.c_str()); } bool mitk::PropertyList::Get(const char *propertyKey, bool &boolValue) const { return this->GetBoolProperty(propertyKey, boolValue); } bool mitk::PropertyList::Get(const char *propertyKey, int &intValue) const { return this->GetIntProperty(propertyKey, intValue); } bool mitk::PropertyList::Get(const char *propertyKey, float &floatValue) const { return this->GetFloatProperty(propertyKey, floatValue); } bool mitk::PropertyList::Get(const char *propertyKey, double &doubleValue) const { return this->GetDoubleProperty(propertyKey, doubleValue); } bool mitk::PropertyList::Get(const char *propertyKey, std::string &stringValue) const { return this->GetStringProperty(propertyKey, stringValue); } bool mitk::PropertyList::GetDoubleProperty(const char *propertyKey, double &doubleValue) const { DoubleProperty *gp = dynamic_cast<DoubleProperty *>(GetProperty(propertyKey)); if (gp != nullptr) { doubleValue = gp->GetValue(); return true; } return false; } void mitk::PropertyList::SetDoubleProperty(const char *propertyKey, double doubleValue) { 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<mitk::IPropertyDeserialization> service(mitk::CoreServices::GetPropertyDeserialization()); + PropertyMap properties; + + auto jObject = j.get<nlohmann::json::object_t>(); + + 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<nlohmann::json::object_t>(); + + for (const auto& [name, serializedProperty] : serializedPropertiesObject) + { + BaseProperty::Pointer property = static_cast<BaseProperty*>(prototype->CreateAnother().GetPointer()); + property->FromJSON(serializedProperty); + properties[name] = property; + } + } + + m_Properties = properties; +} diff --git a/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp b/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp index 0ca671f20d..dad9fa882d 100644 --- a/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkSmartPointerProperty.cpp @@ -1,136 +1,146 @@ /*============================================================================ 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 "mitkSmartPointerProperty.h" mitk::SmartPointerProperty::ReferenceCountMapType mitk::SmartPointerProperty::m_ReferenceCount; mitk::SmartPointerProperty::ReferencesUIDMapType mitk::SmartPointerProperty::m_ReferencesUID; mitk::SmartPointerProperty::ReadInSmartPointersMapType mitk::SmartPointerProperty::m_ReadInInstances; mitk::SmartPointerProperty::ReadInTargetsMapType mitk::SmartPointerProperty::m_ReadInTargets; mitk::UIDGenerator mitk::SmartPointerProperty::m_UIDGenerator("POINTER_"); void mitk::SmartPointerProperty::PostProcessXMLReading() { for (auto iter = m_ReadInInstances.begin(); iter != m_ReadInInstances.end(); ++iter) { if (m_ReadInTargets.find(iter->second) != m_ReadInTargets.end()) { iter->first->SetSmartPointer(m_ReadInTargets[iter->second]); } } m_ReadInInstances.clear(); } /// \return The number of SmartPointerProperties that point to @param object unsigned int mitk::SmartPointerProperty::GetReferenceCountFor(itk::Object *object) { if (m_ReferenceCount.find(object) != m_ReferenceCount.end()) { return m_ReferenceCount[object]; } else { return 0; } } void mitk::SmartPointerProperty::RegisterPointerTarget(itk::Object *object, const std::string uid) { m_ReadInTargets[uid] = object; } std::string mitk::SmartPointerProperty::GetReferenceUIDFor(itk::Object *object) { if (m_ReferencesUID.find(object) != m_ReferencesUID.end()) { return m_ReferencesUID[object]; } else { return std::string("invalid"); } } bool mitk::SmartPointerProperty::IsEqual(const BaseProperty &property) const { return this->m_SmartPointer == static_cast<const Self &>(property).m_SmartPointer; } bool mitk::SmartPointerProperty::Assign(const BaseProperty &property) { this->m_SmartPointer = static_cast<const Self &>(property).m_SmartPointer; return true; } mitk::SmartPointerProperty::SmartPointerProperty(itk::Object *pointer) { SetSmartPointer(pointer); } mitk::SmartPointerProperty::SmartPointerProperty(const SmartPointerProperty &other) : BaseProperty(other), m_SmartPointer(other.m_SmartPointer) { } itk::Object::Pointer mitk::SmartPointerProperty::GetSmartPointer() const { return m_SmartPointer; } itk::Object::Pointer mitk::SmartPointerProperty::GetValue() const { return this->GetSmartPointer(); } void mitk::SmartPointerProperty::SetSmartPointer(itk::Object *pointer) { if (m_SmartPointer.GetPointer() != pointer) { // keep track of referenced objects if (m_SmartPointer.GetPointer() && --m_ReferenceCount[m_SmartPointer.GetPointer()] == 0) // if there is no reference left, delete entry { m_ReferenceCount.erase(m_SmartPointer.GetPointer()); m_ReferencesUID.erase(m_SmartPointer.GetPointer()); } if (pointer && ++m_ReferenceCount[pointer] == 1) // first reference --> generate UID { m_ReferencesUID[pointer] = m_UIDGenerator.GetUID(); } // change pointer m_SmartPointer = pointer; Modified(); } } void mitk::SmartPointerProperty::SetValue(const ValueType &value) { this->SetSmartPointer(value.GetPointer()); } std::string mitk::SmartPointerProperty::GetValueAsString() const { if (m_SmartPointer.IsNotNull()) return m_ReferencesUID[m_SmartPointer.GetPointer()]; else 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)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkStringProperty.cpp b/Modules/Core/src/DataManagement/mitkStringProperty.cpp index 767d6101c1..cc1ecf9e69 100644 --- a/Modules/Core/src/DataManagement/mitkStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkStringProperty.cpp @@ -1,51 +1,64 @@ /*============================================================================ 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 "mitkStringProperty.h" +#include <nlohmann/json.hpp> const char *mitk::StringProperty::PATH = "path"; mitk::StringProperty::StringProperty(const char *string) : m_Value() { if (string) m_Value = string; } mitk::StringProperty::StringProperty(const std::string &s) : m_Value(s) { } mitk::StringProperty::StringProperty(const StringProperty &other) : BaseProperty(other), m_Value(other.m_Value) { } bool mitk::StringProperty::IsEqual(const BaseProperty &property) const { return this->m_Value == static_cast<const Self &>(property).m_Value; } bool mitk::StringProperty::Assign(const BaseProperty &property) { this->m_Value = static_cast<const Self &>(property).m_Value; return true; } std::string mitk::StringProperty::GetValueAsString() const { return m_Value; } +bool mitk::StringProperty::ToJSON(nlohmann::json& j) const +{ + j = this->GetValueAsString(); + return true; +} + +bool mitk::StringProperty::FromJSON(const nlohmann::json& j) +{ + this->SetValue(j.get<std::string>()); + return true; +} + itk::LightObject::Pointer mitk::StringProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp index a3e0b1f549..4764497413 100644 --- a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp @@ -1,493 +1,426 @@ /*============================================================================ 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 <iterator> #include <set> #include <type_traits> #include "mitkTemporoSpatialStringProperty.h" #include <nlohmann/json.hpp> -using namespace nlohmann; +using CondensedTimeKeyType = std::pair<mitk::TimeStepType, mitk::TimeStepType>; +using CondensedTimePointsType = std::map<CondensedTimeKeyType, std::string>; + +using CondensedSliceKeyType = std::pair<mitk::TemporoSpatialStringProperty::IndexValueType, mitk::TemporoSpatialStringProperty::IndexValueType>; +using CondensedSlicesType = std::map<CondensedSliceKeyType, CondensedTimePointsType>; + +namespace +{ + /** Helper function that checks if between an ID and a successive ID is no gap.*/ + template<typename TValue> + bool isGap(const TValue& value, const TValue& successor) + { + return value<successor || value > successor + 1; + } + + + template<typename TNewKey, typename TNewValue, typename TMasterKey, typename TMasterValue, typename TCondensedContainer> + 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) { if (s) { SliceMapType slices{{0, s}}; m_Values.insert(std::make_pair(0, slices)); } } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const std::string &s) { SliceMapType slices{{0, s}}; m_Values.insert(std::make_pair(0, slices)); } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const TemporoSpatialStringProperty &other) : BaseProperty(other), m_Values(other.m_Values) { } bool mitk::TemporoSpatialStringProperty::IsEqual(const BaseProperty &property) const { return this->m_Values == static_cast<const Self &>(property).m_Values; } bool mitk::TemporoSpatialStringProperty::Assign(const BaseProperty &property) { this->m_Values = static_cast<const Self &>(property).m_Values; return true; } std::string mitk::TemporoSpatialStringProperty::GetValueAsString() const { return GetValue(); } bool mitk::TemporoSpatialStringProperty::IsUniform() const { auto refValue = this->GetValue(); for (const auto& timeStep : m_Values) { auto finding = std::find_if_not(timeStep.second.begin(), timeStep.second.end(), [&refValue](const mitk::TemporoSpatialStringProperty::SliceMapType::value_type& val) { return val.second == refValue; }); if (finding != timeStep.second.end()) { return false; } } return true; } itk::LightObject::Pointer mitk::TemporoSpatialStringProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue() const { std::string result = ""; if (!m_Values.empty()) { if (!m_Values.begin()->second.empty()) { result = m_Values.begin()->second.begin()->second; } } return result; }; std::pair<bool, mitk::TemporoSpatialStringProperty::ValueType> mitk::TemporoSpatialStringProperty::CheckValue( const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { std::string value = ""; bool found = false; auto timeIter = m_Values.find(timeStep); auto timeEnd = m_Values.end(); if (timeIter == timeEnd && allowCloseTime) { // search for closest time step (earlier preverd) timeIter = m_Values.upper_bound(timeStep); if (timeIter != m_Values.begin()) { // there is a key lower than time step timeIter = std::prev(timeIter); } } if (timeIter != timeEnd) { const SliceMapType &slices = timeIter->second; auto sliceIter = slices.find(zSlice); auto sliceEnd = slices.end(); if (sliceIter == sliceEnd && allowCloseSlice) { // search for closest slice (earlier preverd) sliceIter = slices.upper_bound(zSlice); if (sliceIter != slices.begin()) { // there is a key lower than slice sliceIter = std::prev(sliceIter); } } if (sliceIter != sliceEnd) { value = sliceIter->second; found = true; } } return std::make_pair(found, value); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).second; }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueBySlice( const IndexValueType &zSlice, bool allowClose) const { return GetValue(0, zSlice, true, allowClose); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueByTimeStep( const TimeStepType &timeStep, bool allowClose) const { return GetValue(timeStep, 0, allowClose, true); }; bool mitk::TemporoSpatialStringProperty::HasValue() const { return !m_Values.empty(); }; bool mitk::TemporoSpatialStringProperty::HasValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).first; }; bool mitk::TemporoSpatialStringProperty::HasValueBySlice(const IndexValueType &zSlice, bool allowClose) const { return HasValue(0, zSlice, true, allowClose); }; bool mitk::TemporoSpatialStringProperty::HasValueByTimeStep(const TimeStepType &timeStep, bool allowClose) const { return HasValue(timeStep, 0, allowClose, true); }; std::vector<mitk::TemporoSpatialStringProperty::IndexValueType> mitk::TemporoSpatialStringProperty::GetAvailableSlices() const { std::set<IndexValueType> uniqueSlices; for (const auto& timeStep : m_Values) { for (const auto& slice : timeStep.second) { uniqueSlices.insert(slice.first); } } return std::vector<IndexValueType>(std::begin(uniqueSlices), std::end(uniqueSlices)); } std::vector<mitk::TemporoSpatialStringProperty::IndexValueType> mitk::TemporoSpatialStringProperty::GetAvailableSlices( const TimeStepType &timeStep) const { std::vector<IndexValueType> result; auto timeIter = m_Values.find(timeStep); auto timeEnd = m_Values.end(); if (timeIter != timeEnd) { for (auto const &element : timeIter->second) { result.push_back(element.first); } } return result; }; std::vector<mitk::TimeStepType> mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps() const { std::vector<mitk::TimeStepType> result; for (auto const &element : m_Values) { result.push_back(element.first); } return result; }; std::vector<mitk::TimeStepType> mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps(const IndexValueType& slice) const { std::vector<mitk::TimeStepType> result; for (const auto& timeStep : m_Values) { if (timeStep.second.find(slice) != std::end(timeStep.second)) { result.push_back(timeStep.first); } } return result; } void mitk::TemporoSpatialStringProperty::SetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, const ValueType &value) { auto timeIter = m_Values.find(timeStep); auto timeEnd = m_Values.end(); if (timeIter == timeEnd) { SliceMapType slices{{zSlice, value}}; m_Values.insert(std::make_pair(timeStep, slices)); } else { timeIter->second[zSlice] = value; } this->Modified(); }; void mitk::TemporoSpatialStringProperty::SetValue(const ValueType &value) { this->Modified(); m_Values.clear(); 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 <class Ch> -std::basic_string<Ch> CreateJSONEscapes(const std::basic_string<Ch> &s) +bool mitk::TemporoSpatialStringProperty::ToJSON(nlohmann::json& j) const { - std::basic_string<Ch> result; - typename std::basic_string<Ch>::const_iterator b = s.begin(); - typename std::basic_string<Ch>::const_iterator e = s.end(); - while (b != e) - { - using UCh = std::make_unsigned_t<Ch>; - 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<unsigned long>(static_cast<UCh>(*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<mitk::TimeStepType, mitk::TimeStepType>; -using CondensedTimePointsType = std::map<CondensedTimeKeyType, std::string>; - -using CondensedSliceKeyType = std::pair<mitk::TemporoSpatialStringProperty::IndexValueType, mitk::TemporoSpatialStringProperty::IndexValueType>; -using CondensedSlicesType = std::map<CondensedSliceKeyType, CondensedTimePointsType>; - -/** Helper function that checks if between an ID and a successive ID is no gap.*/ -template<typename TValue> -bool isGap(const TValue& value, const TValue& successor) -{ - return value<successor || value > successor + 1; -} - - -template<typename TNewKey, typename TNewValue, typename TMasterKey, typename TMasterValue, typename TCondensedContainer> -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<const mitk::TemporoSpatialStringProperty *>(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["z"] = minSliceID; + if (minSliceID != maxSliceID) - { - stream << "\"zmax\":" << maxSliceID << ", "; - } - stream << "\"t\":" << minTimePointID << ", "; + value["zmax"] = maxSliceID; + + value["t"] = minTimePointID; + if (minTimePointID != maxTimePointID) - { - stream << "\"tmax\":" << maxTimePointID << ", "; - } + value["tmax"] = maxTimePointID; + + value["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<TemporoSpatialStringProperty::IndexValueType>("z", 0); auto zmax = element.value<TemporoSpatialStringProperty::IndexValueType>("zmax", z); auto t = element.value<TimeStepType>("t", 0); auto tmax = element.value<TimeStepType>("tmax", t); for (auto currentT = t; currentT <= tmax; ++currentT) { 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<const mitk::TemporoSpatialStringProperty *>(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 3e5bce2ca6..20a59af2a7 100644 --- a/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTransferFunctionProperty.cpp @@ -1,53 +1,125 @@ /*============================================================================ 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 "mitkTransferFunctionProperty.h" +#include <nlohmann/json.hpp> namespace mitk { bool TransferFunctionProperty::IsEqual(const BaseProperty &property) const { return *(this->m_Value) == *(static_cast<const Self &>(property).m_Value); } bool TransferFunctionProperty::Assign(const BaseProperty &property) { this->m_Value = static_cast<const Self &>(property).m_Value; return true; } std::string TransferFunctionProperty::GetValueAsString() const { std::stringstream myStr; myStr << GetValue(); return myStr.str(); } TransferFunctionProperty::TransferFunctionProperty() : BaseProperty(), m_Value(mitk::TransferFunction::New()) {} TransferFunctionProperty::TransferFunctionProperty(const TransferFunctionProperty &other) : BaseProperty(other), m_Value(other.m_Value->Clone()) { } TransferFunctionProperty::TransferFunctionProperty(mitk::TransferFunction::Pointer value) : BaseProperty(), m_Value(value) { } + 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<double, 6> 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<double, 6> 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)); result->UnRegister(); return result; } } // namespace mitk diff --git a/Modules/Core/src/DataManagement/mitkVectorProperty.cpp b/Modules/Core/src/DataManagement/mitkVectorProperty.cpp index d2fa075f52..f6e986629f 100644 --- a/Modules/Core/src/DataManagement/mitkVectorProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkVectorProperty.cpp @@ -1,90 +1,105 @@ /*============================================================================ 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 "mitkVectorProperty.h" +#include <nlohmann/json.hpp> namespace mitk { template <typename DATATYPE> bool VectorProperty<DATATYPE>::IsEqual(const BaseProperty &property) const { return this->m_PropertyContent == static_cast<const Self &>(property).m_PropertyContent; } template <typename DATATYPE> bool VectorProperty<DATATYPE>::Assign(const BaseProperty &property) { this->m_PropertyContent = static_cast<const Self &>(property).m_PropertyContent; return true; } template <typename DATATYPE> itk::LightObject::Pointer VectorProperty<DATATYPE>::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); return result; } template <typename DATATYPE> std::string VectorProperty<DATATYPE>::GetValueAsString() const { const size_t displayBlockLength = 3; size_t beginningElementsCount = displayBlockLength; size_t endElementsCount = displayBlockLength; if (m_PropertyContent.size() <= 2 * displayBlockLength) { beginningElementsCount = m_PropertyContent.size(); endElementsCount = 0; } // return either a block of all items // if the total number of maximum 2*displayBlockLength // // or return the first and last "displayBlockLength" // number of items separated by "[...]"; std::stringstream string_collector; for (size_t i = 0; i < beginningElementsCount; i++) string_collector << m_PropertyContent[i] << "\n"; if (endElementsCount) string_collector << "[... " << m_PropertyContent.size() - 2 * displayBlockLength << " more]\n"; for (size_t i = m_PropertyContent.size() - endElementsCount; i < m_PropertyContent.size(); ++i) string_collector << m_PropertyContent[i] << "\n"; std::string return_value = string_collector.str(); // remove last '\n' if (!return_value.empty()) return_value.erase(return_value.size() - 1); return return_value; } template <typename DATATYPE> void VectorProperty<DATATYPE>::SetValue(const VectorType &newValue) { m_PropertyContent = newValue; } template <typename DATATYPE> const typename VectorProperty<DATATYPE>::VectorType &VectorProperty<DATATYPE>::GetValue() const { return m_PropertyContent; } + template <typename DATATYPE> + bool VectorProperty<DATATYPE>::ToJSON(nlohmann::json& j) const + { + j = this->GetValue(); + return true; + } + + template <typename DATATYPE> + bool VectorProperty<DATATYPE>::FromJSON(const nlohmann::json& j) + { + this->SetValue(j.get<VectorType>()); + return true; + } + // Explicit instantiation for defined types. MITK_DEFINE_VECTOR_PROPERTY(double) MITK_DEFINE_VECTOR_PROPERTY(int) MITK_DEFINE_VECTOR_PROPERTY(unsigned int) MITK_DEFINE_VECTOR_PROPERTY(std::string) } // namespace mitk diff --git a/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp b/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp index 33249b73cf..e3b8f46fde 100644 --- a/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkWeakPointerProperty.cpp @@ -1,75 +1,85 @@ /*============================================================================ 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 "mitkWeakPointerProperty.h" bool mitk::WeakPointerProperty::IsEqual(const BaseProperty &property) const { return this->m_WeakPointer == static_cast<const Self &>(property).m_WeakPointer; } bool mitk::WeakPointerProperty::Assign(const BaseProperty &property) { this->m_WeakPointer = static_cast<const Self &>(property).m_WeakPointer; return true; } mitk::WeakPointerProperty::WeakPointerProperty(itk::Object *pointer) : m_WeakPointer(pointer) { } mitk::WeakPointerProperty::WeakPointerProperty(const WeakPointerProperty &other) : mitk::BaseProperty(other), m_WeakPointer(other.m_WeakPointer) { } mitk::WeakPointerProperty::~WeakPointerProperty() { } std::string mitk::WeakPointerProperty::GetValueAsString() const { std::stringstream ss; ss << m_WeakPointer.GetPointer(); return ss.str(); } mitk::WeakPointerProperty::ValueType mitk::WeakPointerProperty::GetWeakPointer() const { return m_WeakPointer.GetPointer(); } mitk::WeakPointerProperty::ValueType mitk::WeakPointerProperty::GetValue() const { return GetWeakPointer(); } void mitk::WeakPointerProperty::SetWeakPointer(itk::Object *pointer) { if (m_WeakPointer.GetPointer() != pointer) { m_WeakPointer = pointer; Modified(); } } 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)); result->UnRegister(); return result; } diff --git a/Modules/Core/src/IO/mitkImageGenerator.cpp b/Modules/Core/src/IO/mitkImageGenerator.cpp deleted file mode 100644 index f92933ddeb..0000000000 --- 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. - -============================================================================*/ diff --git a/Modules/Core/src/Rendering/mitkAnnotation.cpp b/Modules/Core/src/Rendering/mitkAnnotation.cpp index c9ac002908..9568fc0541 100644 --- a/Modules/Core/src/Rendering/mitkAnnotation.cpp +++ b/Modules/Core/src/Rendering/mitkAnnotation.cpp @@ -1,348 +1,349 @@ /*============================================================================ 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 "mitkAnnotation.h" -#include "usGetModuleContext.h" +#include <mitkAnnotation.h> +#include <mitkUIDGenerator.h> +#include <usGetModuleContext.h> const std::string mitk::Annotation::US_INTERFACE_NAME = "org.mitk.services.Annotation"; const std::string mitk::Annotation::US_PROPKEY_AnnotationNAME = US_INTERFACE_NAME + ".name"; const std::string mitk::Annotation::US_PROPKEY_ID = US_INTERFACE_NAME + ".id"; const std::string mitk::Annotation::US_PROPKEY_MODIFIED = US_INTERFACE_NAME + ".modified"; const std::string mitk::Annotation::US_PROPKEY_RENDERER_ID = US_INTERFACE_NAME + ".rendererId"; const std::string mitk::Annotation::US_PROPKEY_AR_ID = US_INTERFACE_NAME + ".arId"; mitk::Annotation::Annotation() : m_PropertyListModifiedObserverTag(0) { m_PropertyList = mitk::PropertyList::New(); itk::MemberCommand<mitk::Annotation>::Pointer _PropertyListModifiedCommand = itk::MemberCommand<mitk::Annotation>::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::Annotation::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); this->SetName(this->GetNameOfClass()); this->SetVisibility(true); this->SetOpacity(1.0); } void mitk::Annotation::PropertyListModified(const itk::Object * /*caller*/, const itk::EventObject &) { AnnotationModified(); } mitk::Annotation::~Annotation() { this->UnRegisterMicroservice(); } void mitk::Annotation::SetUSProperty(const std::string &propertyKey, us::Any value) { if (this->m_ServiceRegistration) { us::ServiceProperties props; std::vector<std::string> propertyKeys; m_ServiceRegistration.GetReference().GetPropertyKeys(propertyKeys); for (const std::string &key : propertyKeys) { props[key] = m_ServiceRegistration.GetReference().GetProperty(key); } props[propertyKey] = value; m_ServiceRegistration.SetProperties(props); } } void mitk::Annotation::SetProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue) { this->m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::Annotation::ReplaceProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue) { this->m_PropertyList->ReplaceProperty(propertyKey, propertyValue); } void mitk::Annotation::AddProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue, bool overwrite) { if ((overwrite) || (GetProperty(propertyKey) == nullptr)) { SetProperty(propertyKey, propertyValue); } } void mitk::Annotation::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty *mitk::Annotation::GetProperty(const std::string &propertyKey) const { mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty(propertyKey); if (property.IsNotNull()) return property; // only to satisfy compiler! return nullptr; } bool mitk::Annotation::GetBoolProperty(const std::string &propertyKey, bool &boolValue) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast<mitk::BoolProperty *>(GetProperty(propertyKey)); if (boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::Annotation::GetIntProperty(const std::string &propertyKey, int &intValue) const { mitk::IntProperty::Pointer intprop = dynamic_cast<mitk::IntProperty *>(GetProperty(propertyKey)); if (intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::Annotation::GetFloatProperty(const std::string &propertyKey, float &floatValue) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast<mitk::FloatProperty *>(GetProperty(propertyKey)); if (floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::Annotation::GetStringProperty(const std::string &propertyKey, std::string &string) const { mitk::StringProperty::Pointer stringProp = dynamic_cast<mitk::StringProperty *>(GetProperty(propertyKey)); if (stringProp.IsNull()) { return false; } else { // memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } void mitk::Annotation::SetIntProperty(const std::string &propertyKey, int intValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); Modified(); } void mitk::Annotation::SetBoolProperty(const std::string &propertyKey, bool boolValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); Modified(); } void mitk::Annotation::SetFloatProperty(const std::string &propertyKey, float floatValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); Modified(); } void mitk::Annotation::SetDoubleProperty(const std::string &propertyKey, double doubleValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); Modified(); } void mitk::Annotation::SetStringProperty(const std::string &propertyKey, const std::string &stringValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); Modified(); } std::string mitk::Annotation::GetName() const { mitk::StringProperty *sp = dynamic_cast<mitk::StringProperty *>(this->GetProperty("name")); if (sp == nullptr) return ""; return sp->GetValue(); } void mitk::Annotation::SetName(const std::string &name) { this->SetStringProperty("name", name); } bool mitk::Annotation::GetName(std::string &nodeName, const std::string &propertyKey) const { return GetStringProperty(propertyKey, nodeName); } void mitk::Annotation::SetText(std::string text) { SetStringProperty("Text", text.c_str()); } std::string mitk::Annotation::GetText() const { std::string text; GetStringProperty("Text", text); return text; } void mitk::Annotation::SetFontSize(int fontSize) { SetIntProperty("FontSize", fontSize); } int mitk::Annotation::GetFontSize() const { int fontSize = 1; GetIntProperty("FontSize", fontSize); return fontSize; } bool mitk::Annotation::GetVisibility(bool &visible, const std::string &propertyKey) const { return GetBoolProperty(propertyKey, visible); } bool mitk::Annotation::IsVisible(const std::string &propertyKey, bool defaultIsOn) const { return IsOn(propertyKey, defaultIsOn); } bool mitk::Annotation::GetColor(float rgb[], const std::string &propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast<mitk::ColorProperty *>(GetProperty(propertyKey)); if (colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); return true; } void mitk::Annotation::SetColor(const mitk::Color &color, const std::string &propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); this->m_PropertyList->SetProperty(propertyKey, prop); } void mitk::Annotation::SetColor(float red, float green, float blue, const std::string &propertyKey) { float color[3]; color[0] = red; color[1] = green; color[2] = blue; SetColor(color, propertyKey); } void mitk::Annotation::SetColor(const float rgb[], const std::string &propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); this->m_PropertyList->SetProperty(propertyKey, prop); } bool mitk::Annotation::GetOpacity(float &opacity, const std::string &propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast<mitk::FloatProperty *>(GetProperty(propertyKey)); if (opacityprop.IsNull()) return false; opacity = opacityprop->GetValue(); return true; } void mitk::Annotation::SetOpacity(float opacity, const std::string &propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); this->m_PropertyList->SetProperty(propertyKey, prop); } void mitk::Annotation::SetVisibility(bool visible, const std::string &propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); this->m_PropertyList->SetProperty(propertyKey, prop); Modified(); } bool mitk::Annotation::BaseLocalStorage::IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Annotation *Annotation) { if (m_LastGenerateDataTime < Annotation->GetMTime()) return true; if (m_LastGenerateDataTime < Annotation->GetPropertyList()->GetMTime()) return true; if (renderer && m_LastGenerateDataTime < renderer->GetTimeStepUpdateTime()) return true; return false; } mitk::Annotation::Bounds mitk::Annotation::GetBoundsOnDisplay(mitk::BaseRenderer *) const { mitk::Annotation::Bounds bounds; bounds.Position[0] = bounds.Position[1] = bounds.Size[0] = bounds.Size[1] = 0; return bounds; } void mitk::Annotation::SetBoundsOnDisplay(mitk::BaseRenderer *, const mitk::Annotation::Bounds &) { } void mitk::Annotation::SetForceInForeground(bool forceForeground) { m_ForceInForeground = forceForeground; } bool mitk::Annotation::IsForceInForeground() const { return m_ForceInForeground; } mitk::PropertyList *mitk::Annotation::GetPropertyList() const { return m_PropertyList; } std::string mitk::Annotation::GetMicroserviceID() { return this->m_ServiceRegistration.GetReference().GetProperty(US_PROPKEY_ID).ToString(); } void mitk::Annotation::RegisterAsMicroservice(us::ServiceProperties props) { if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); us::ModuleContext *context = us::GetModuleContext(); // Define ServiceProps mitk::UIDGenerator uidGen = mitk::UIDGenerator("org.mitk.services.Annotation.id_"); props[US_PROPKEY_ID] = uidGen.GetUID(); m_ServiceRegistration = context->RegisterService(this, props); } void mitk::Annotation::UnRegisterMicroservice() { if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } void mitk::Annotation::AnnotationModified() { Modified(); this->SetUSProperty(US_PROPKEY_MODIFIED, this->GetMTime()); } diff --git a/Modules/Core/src/mitkCoreActivator.cpp b/Modules/Core/src/mitkCoreActivator.cpp index 594cdf5490..a5c00479ad 100644 --- a/Modules/Core/src/mitkCoreActivator.cpp +++ b/Modules/Core/src/mitkCoreActivator.cpp @@ -1,368 +1,437 @@ /*============================================================================ 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 "mitkCoreActivator.h" #include <mitkCoreServices.h> #include <mitkPropertyPersistenceInfo.h> // File IO #include <mitkIOMetaInformationPropertyConstants.h> #include <mitkGeometryDataReaderService.h> #include <mitkGeometryDataWriterService.h> #include <mitkIOMimeTypes.h> #include <mitkIOUtil.h> #include <mitkImageVtkLegacyIO.h> #include <mitkImageVtkXmlIO.h> #include <mitkItkImageIO.h> #include <mitkMimeTypeProvider.h> #include <mitkPointSetReaderService.h> #include <mitkPointSetWriterService.h> #include <mitkRawImageFileReader.h> #include <mitkSurfaceStlIO.h> #include <mitkSurfaceVtkLegacyIO.h> #include <mitkSurfaceVtkXmlIO.h> #include "mitkLegacyFileWriterService.h" #include <mitkFileWriter.h> #include <itkGDCMImageIO.h> #include <itkNiftiImageIO.h> // PropertyRelationRules #include <mitkPropertyRelationRuleBase.h> // Micro Services #include <usGetModuleContext.h> #include <usModule.h> #include <usModuleActivator.h> #include <usModuleContext.h> #include <usModuleEvent.h> #include <usModuleInitialization.h> #include <usModuleResource.h> #include <usModuleResourceStream.h> #include <usModuleSettings.h> #include <usServiceTracker.h> +// Properties +#include <mitkAnnotationProperty.h> +#include <mitkClippingProperty.h> +#include <mitkColorProperty.h> +#include <mitkGroupTagProperty.h> +#include <mitkLevelWindowProperty.h> +#include <mitkLookupTableProperty.h> +#include <mitkModalityProperty.h> +#include <mitkPlaneOrientationProperty.h> +#include <mitkPointSetShapeProperty.h> +#include <mitkProperties.h> +#include <mitkRenderingModeProperty.h> +#include <mitkStringProperty.h> +#include <mitkTemporoSpatialStringProperty.h> +#include <mitkTransferFunctionProperty.h> +#include <mitkVectorProperty.h> +#include <mitkVtkInterpolationProperty.h> +#include <mitkVtkRepresentationProperty.h> +#include <mitkVtkResliceInterpolationProperty.h> +#include <mitkVtkScalarModeProperty.h> + // ITK "injects" static initialization code for IO factories // via the itkImageIOFactoryRegisterManager.h header (which // is generated in the application library build directory). // To ensure that the code is called *before* the CppMicroServices // static initialization code (which triggers the Activator::Start // method), we include the ITK header here. #include <itkImageIOFactoryRegisterManager.h> -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; case us::InfoMsg: MITK_INFO << msg; break; case us::WarningMsg: MITK_WARN << msg; break; 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<mitk::IPropertyPersistence> persistenceService(mitk::CoreServices::GetPropertyPersistence()); -void AddPropertyPersistence(const mitk::PropertyKeyPath& propPath) -{ - mitk::CoreServicePointer<mitk::IPropertyPersistence> 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<mitk::IPropertyDeserialization> service(mitk::CoreServices::GetPropertyDeserialization()); + + service->RegisterProperty<mitk::AnnotationProperty>(); + service->RegisterProperty<mitk::ClippingProperty>(); + service->RegisterProperty<mitk::ColorProperty>(); + service->RegisterProperty<mitk::BoolProperty>(); + service->RegisterProperty<mitk::BoolLookupTableProperty>(); + service->RegisterProperty<mitk::DoubleProperty>(); + service->RegisterProperty<mitk::DoubleVectorProperty>(); + service->RegisterProperty<mitk::FloatProperty>(); + service->RegisterProperty<mitk::FloatLookupTableProperty>(); + service->RegisterProperty<mitk::GroupTagProperty>(); + service->RegisterProperty<mitk::IntProperty>(); + service->RegisterProperty<mitk::IntLookupTableProperty>(); + service->RegisterProperty<mitk::IntVectorProperty>(); + service->RegisterProperty<mitk::LevelWindowProperty>(); + service->RegisterProperty<mitk::LookupTableProperty>(); + service->RegisterProperty<mitk::PlaneOrientationProperty>(); + service->RegisterProperty<mitk::Point2dProperty>(); + service->RegisterProperty<mitk::Point3dProperty>(); + service->RegisterProperty<mitk::Point3iProperty>(); + service->RegisterProperty<mitk::Point4dProperty>(); + service->RegisterProperty<mitk::PointSetShapeProperty>(); + service->RegisterProperty<mitk::ModalityProperty>(); + service->RegisterProperty<mitk::RenderingModeProperty>(); + service->RegisterProperty<mitk::StringProperty>(); + service->RegisterProperty<mitk::StringLookupTableProperty>(); + service->RegisterProperty<mitk::TemporoSpatialStringProperty>(); + service->RegisterProperty<mitk::TransferFunctionProperty>(); + service->RegisterProperty<mitk::UIntProperty>(); + service->RegisterProperty<mitk::UShortProperty>(); + service->RegisterProperty<mitk::Vector3DProperty>(); + service->RegisterProperty<mitk::VtkInterpolationProperty>(); + service->RegisterProperty<mitk::VtkRepresentationProperty>(); + service->RegisterProperty<mitk::VtkResliceInterpolationProperty>(); + service->RegisterProperty<mitk::VtkScalarModeProperty>(); } - - persistenceService->AddInfo(info); } class FixedNiftiImageIO : public itk::NiftiImageIO { public: /** Standard class typedefs. */ typedef FixedNiftiImageIO Self; typedef itk::NiftiImageIO Superclass; typedef itk::SmartPointer<Self> Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(FixedNiftiImageIO, Superclass) bool SupportsDimension(unsigned long dim) override { return dim > 1 && dim < 5; } }; void MitkCoreActivator::Load(us::ModuleContext *context) { // Handle messages from CppMicroServices us::installMsgHandler(HandleMicroServicesMessages); this->m_Context = context; // Add the current application directory to the auto-load paths. // This is useful for third-party executables. std::string programPath = mitk::IOUtil::GetProgramPath(); if (programPath.empty()) { MITK_WARN << "Could not get the program path."; } else { AddMitkAutoLoadPaths(programPath); } // m_RenderingManager = mitk::RenderingManager::New(); // context->RegisterService<mitk::RenderingManager>(renderingManager.GetPointer()); m_PlanePositionManager.reset(new mitk::PlanePositionManagerService); context->RegisterService<mitk::PlanePositionManagerService>(m_PlanePositionManager.get()); m_PropertyAliases.reset(new mitk::PropertyAliases); context->RegisterService<mitk::IPropertyAliases>(m_PropertyAliases.get()); m_PropertyDescriptions.reset(new mitk::PropertyDescriptions); context->RegisterService<mitk::IPropertyDescriptions>(m_PropertyDescriptions.get()); + m_PropertyDeserialization.reset(new mitk::PropertyDeserialization); + context->RegisterService<mitk::IPropertyDeserialization>(m_PropertyDeserialization.get()); + m_PropertyExtensions.reset(new mitk::PropertyExtensions); context->RegisterService<mitk::IPropertyExtensions>(m_PropertyExtensions.get()); m_PropertyFilters.reset(new mitk::PropertyFilters); context->RegisterService<mitk::IPropertyFilters>(m_PropertyFilters.get()); m_PropertyPersistence.reset(new mitk::PropertyPersistence); context->RegisterService<mitk::IPropertyPersistence>(m_PropertyPersistence.get()); m_PropertyRelations.reset(new mitk::PropertyRelations); context->RegisterService<mitk::IPropertyRelations>(m_PropertyRelations.get()); m_PreferencesService.reset(new mitk::PreferencesService); context->RegisterService<mitk::IPreferencesService>(m_PreferencesService.get()); m_MimeTypeProvider.reset(new mitk::MimeTypeProvider); m_MimeTypeProvider->Start(); m_MimeTypeProviderReg = context->RegisterService<mitk::IMimeTypeProvider>(m_MimeTypeProvider.get()); this->RegisterDefaultMimeTypes(); this->RegisterItkReaderWriter(); this->RegisterVtkReaderWriter(); // Add custom Reader / Writer Services m_FileReaders.push_back(new mitk::PointSetReaderService()); m_FileWriters.push_back(new mitk::PointSetWriterService()); m_FileReaders.push_back(new mitk::GeometryDataReaderService()); m_FileWriters.push_back(new mitk::GeometryDataWriterService()); m_FileReaders.push_back(new mitk::RawImageFileReaderService()); //add properties that should be persistent (if possible/supported by the writer) AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_DESCRIPTION()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_INPUTLOCATION()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_MIME_CATEGORY()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_MIME_NAME()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_VERSION()); AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_OPTIONS_ANY()); AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIDestinationUIDPropertyKeyPath()); AddPropertyPersistence(mitk::PropertyRelationRuleBase::GetRIIRelationUIDPropertyKeyPath()); 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. vtkNeverTranslucentTextureFactory* textureFactory = vtkNeverTranslucentTextureFactory::New(); vtkObjectFactory::RegisterFactory( textureFactory ); textureFactory->Delete(); */ this->RegisterLegacyWriter(); } void MitkCoreActivator::Unload(us::ModuleContext *) { for (auto &elem : m_FileReaders) { delete elem; } for (auto &elem : m_FileWriters) { delete elem; } for (auto &elem : m_FileIOs) { delete elem; } for (auto &elem : m_LegacyWriters) { delete elem; } // The mitk::ModuleContext* argument of the Unload() method // will always be 0 for the Mitk library. It makes no sense // to use it at this stage anyway, since all libraries which // know about the module system have already been unloaded. // we need to close the internal service tracker of the // MimeTypeProvider class here. Otherwise it // would hold on to the ModuleContext longer than it is // actually valid. m_MimeTypeProviderReg.Unregister(); m_MimeTypeProvider->Stop(); for (std::vector<mitk::CustomMimeType *>::const_iterator mimeTypeIter = m_DefaultMimeTypes.begin(), iterEnd = m_DefaultMimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { delete *mimeTypeIter; } } void MitkCoreActivator::RegisterDefaultMimeTypes() { // Register some default mime-types std::vector<mitk::CustomMimeType *> mimeTypes = mitk::IOMimeTypes::Get(); for (std::vector<mitk::CustomMimeType *>::const_iterator mimeTypeIter = mimeTypes.begin(), iterEnd = mimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { m_DefaultMimeTypes.push_back(*mimeTypeIter); m_Context->RegisterService(m_DefaultMimeTypes.back()); } } void MitkCoreActivator::RegisterItkReaderWriter() { std::list<itk::LightObject::Pointer> allobjects = itk::ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); for (auto &allobject : allobjects) { auto *io = dynamic_cast<itk::ImageIOBase *>(allobject.GetPointer()); // NiftiImageIO does not provide a correct "SupportsDimension()" methods // and the supported read/write extensions are not ordered correctly if (dynamic_cast<itk::NiftiImageIO *>(io)) continue; // Use a custom mime-type for GDCMImageIO below if (dynamic_cast<itk::GDCMImageIO *>(allobject.GetPointer())) { // MITK provides its own DICOM reader (which internally uses GDCMImageIO). continue; } if (io) { m_FileIOs.push_back(new mitk::ItkImageIO(io)); } else { MITK_WARN << "Error ImageIO factory did not return an ImageIOBase: " << (allobject)->GetNameOfClass(); } } FixedNiftiImageIO::Pointer itkNiftiIO = FixedNiftiImageIO::New(); mitk::ItkImageIO *niftiIO = new mitk::ItkImageIO(mitk::IOMimeTypes::NIFTI_MIMETYPE(), itkNiftiIO.GetPointer(), 0); m_FileIOs.push_back(niftiIO); } void MitkCoreActivator::RegisterVtkReaderWriter() { m_FileIOs.push_back(new mitk::SurfaceVtkXmlIO()); m_FileIOs.push_back(new mitk::SurfaceStlIO()); m_FileIOs.push_back(new mitk::SurfaceVtkLegacyIO()); m_FileIOs.push_back(new mitk::ImageVtkXmlIO()); m_FileIOs.push_back(new mitk::ImageVtkLegacyIO()); } void MitkCoreActivator::RegisterLegacyWriter() { std::list<itk::LightObject::Pointer> allobjects = itk::ObjectFactoryBase::CreateAllInstance("IOWriter"); for (auto i = allobjects.begin(); i != allobjects.end(); ++i) { mitk::FileWriter::Pointer io = dynamic_cast<mitk::FileWriter *>(i->GetPointer()); if (io) { std::string description = std::string("Legacy ") + io->GetNameOfClass() + " Writer"; mitk::IFileWriter *writer = new mitk::LegacyFileWriterService(io, description); m_LegacyWriters.push_back(writer); } else { MITK_ERROR << "Error IOWriter override is not of type mitk::FileWriter: " << (*i)->GetNameOfClass() << std::endl; } } } US_EXPORT_MODULE_ACTIVATOR(MitkCoreActivator) // Call CppMicroservices initialization code at the end of the file. // This especially ensures that VTK object factories have already // been registered (VTK initialization code is injected by implicitly // include VTK header files at the top of this file). US_INITIALIZE_MODULE diff --git a/Modules/Core/src/mitkCoreActivator.h b/Modules/Core/src/mitkCoreActivator.h index 19c513bba7..d28f97a70a 100644 --- a/Modules/Core/src/mitkCoreActivator.h +++ b/Modules/Core/src/mitkCoreActivator.h @@ -1,82 +1,84 @@ /*============================================================================ 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 mitkCoreActivator_h #define mitkCoreActivator_h // File IO #include <mitkAbstractFileIO.h> #include <mitkIFileReader.h> #include <mitkIFileWriter.h> #include <mitkMimeTypeProvider.h> #include <mitkPlanePositionManager.h> #include <mitkPropertyAliases.h> #include <mitkPropertyDescriptions.h> +#include <mitkPropertyDeserialization.h> #include <mitkPropertyExtensions.h> #include <mitkPropertyFilters.h> #include <mitkPropertyPersistence.h> #include <mitkPropertyRelations.h> #include <mitkPreferencesService.h> // Micro Services #include <usModuleActivator.h> #include <usModuleEvent.h> #include <usServiceRegistration.h> #include <usServiceTracker.h> #include <memory> /* * This is the module activator for the "Mitk" module. It registers core services * like ... */ class MitkCoreActivator : public us::ModuleActivator { public: void Load(us::ModuleContext *context) override; void Unload(us::ModuleContext *) override; private: void HandleModuleEvent(const us::ModuleEvent moduleEvent); void RegisterDefaultMimeTypes(); void RegisterItkReaderWriter(); void RegisterVtkReaderWriter(); void RegisterLegacyWriter(); // mitk::RenderingManager::Pointer m_RenderingManager; std::unique_ptr<mitk::PlanePositionManagerService> m_PlanePositionManager; std::unique_ptr<mitk::PropertyAliases> m_PropertyAliases; std::unique_ptr<mitk::PropertyDescriptions> m_PropertyDescriptions; + std::unique_ptr<mitk::PropertyDeserialization> m_PropertyDeserialization; std::unique_ptr<mitk::PropertyExtensions> m_PropertyExtensions; std::unique_ptr<mitk::PropertyFilters> m_PropertyFilters; std::unique_ptr<mitk::PropertyPersistence> m_PropertyPersistence; std::unique_ptr<mitk::PropertyRelations> m_PropertyRelations; std::unique_ptr<mitk::MimeTypeProvider> m_MimeTypeProvider; std::unique_ptr<mitk::PreferencesService> m_PreferencesService; // File IO std::vector<mitk::IFileReader *> m_FileReaders; std::vector<mitk::IFileWriter *> m_FileWriters; std::vector<mitk::AbstractFileIO *> m_FileIOs; std::vector<mitk::IFileWriter *> m_LegacyWriters; std::vector<mitk::CustomMimeType *> m_DefaultMimeTypes; us::ServiceRegistration<mitk::IMimeTypeProvider> m_MimeTypeProviderReg; us::ModuleContext *m_Context; }; #endif diff --git a/Modules/Core/src/mitkCoreServices.cpp b/Modules/Core/src/mitkCoreServices.cpp index 720730347f..96dce2bcde 100644 --- a/Modules/Core/src/mitkCoreServices.cpp +++ b/Modules/Core/src/mitkCoreServices.cpp @@ -1,133 +1,139 @@ /*============================================================================ 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 "mitkCoreServices.h" #include <mitkIMimeTypeProvider.h> #include <mitkIPropertyAliases.h> #include <mitkIPropertyDescriptions.h> +#include <mitkIPropertyDeserialization.h> #include <mitkIPropertyExtensions.h> #include <mitkIPropertyFilters.h> #include <mitkIPropertyPersistence.h> #include <mitkIPropertyRelations.h> #include <mitkIPreferencesService.h> #include <usGetModuleContext.h> #include <usModuleContext.h> #include <usServiceReference.h> #include <usServiceTracker.h> #include <mutex> namespace mitk { std::mutex &s_ContextToServicesMapMutex() { static std::mutex mutex; return mutex; } std::map<us::ModuleContext *, std::map<void *, us::ServiceReferenceU>> &s_ContextToServicesMap() { static std::map<us::ModuleContext *, std::map<void *, us::ServiceReferenceU>> serviceMap; return serviceMap; } template <class S> static S *GetCoreService(us::ModuleContext *context) { if (context == nullptr) context = us::GetModuleContext(); S *coreService = nullptr; us::ServiceReference<S> serviceRef = context->GetServiceReference<S>(); if (serviceRef) { coreService = context->GetService(serviceRef); } assert(coreService && "Asserting non-nullptr MITK core service"); { std::lock_guard<std::mutex> l(s_ContextToServicesMapMutex()); s_ContextToServicesMap()[context].insert(std::make_pair(coreService, serviceRef)); } return coreService; } IPropertyAliases *CoreServices::GetPropertyAliases(us::ModuleContext *context) { return GetCoreService<IPropertyAliases>(context); } IPropertyDescriptions *CoreServices::GetPropertyDescriptions(us::ModuleContext *context) { return GetCoreService<IPropertyDescriptions>(context); } + IPropertyDeserialization* CoreServices::GetPropertyDeserialization(us::ModuleContext* context) + { + return GetCoreService<IPropertyDeserialization>(context); + } + IPropertyExtensions *CoreServices::GetPropertyExtensions(us::ModuleContext *context) { return GetCoreService<IPropertyExtensions>(context); } IPropertyFilters *CoreServices::GetPropertyFilters(us::ModuleContext *context) { return GetCoreService<IPropertyFilters>(context); } IPropertyPersistence *CoreServices::GetPropertyPersistence(us::ModuleContext *context) { return GetCoreService<IPropertyPersistence>(context); } IPropertyRelations *CoreServices::GetPropertyRelations(us::ModuleContext *context) { return GetCoreService<IPropertyRelations>(context); } IMimeTypeProvider *CoreServices::GetMimeTypeProvider(us::ModuleContext *context) { return GetCoreService<IMimeTypeProvider>(context); } IPreferencesService *CoreServices::GetPreferencesService(us::ModuleContext *context) { return GetCoreService<IPreferencesService>(context); } bool CoreServices::Unget(us::ModuleContext *context, const std::string & /*interfaceId*/, void *service) { bool success = false; std::lock_guard<std::mutex> l(s_ContextToServicesMapMutex()); auto iter = s_ContextToServicesMap().find(context); if (iter != s_ContextToServicesMap().end()) { auto iter2 = iter->second.find(service); if (iter2 != iter->second.end()) { us::ServiceReferenceU serviceRef = iter2->second; if (serviceRef) { success = context->UngetService(serviceRef); if (success) { iter->second.erase(iter2); } } } } return success; } } diff --git a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp index ae79c6260b..48db4613df 100644 --- a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp +++ b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp @@ -1,242 +1,245 @@ /*============================================================================ 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 "mitkTemporoSpatialStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include <limits> class mitkTemporoSpatialStringPropertyTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkTemporoSpatialStringPropertyTestSuite); MITK_TEST(GetValue); MITK_TEST(HasValue); MITK_TEST(SetValue); MITK_TEST(IsUniform); MITK_TEST(serializeTemporoSpatialStringPropertyToJSON); MITK_TEST(deserializeJSONToTemporoSpatialStringProperty); CPPUNIT_TEST_SUITE_END(); private: std::string refJSON; std::string refJSON_legacy; std::string refPartlyCondensibleJSON; std::string refCondensibleJSON; mitk::TemporoSpatialStringProperty::Pointer refProp; mitk::TemporoSpatialStringProperty::Pointer refPartlyCondensibleProp; mitk::TemporoSpatialStringProperty::Pointer refCondensibleProp; public: void setUp() override { refJSON_legacy = "{\"values\":[{\"t\":0, \"z\":0, \"value\":\"v_0_0\"}, {\"t\":3, \"z\":0, \"value\":\"v_3_0\"}, " "{\"t\":3, \"z\":2, \"value\":\"v_3_2\"}, {\"t\":6, \"z\":1, \"value\":\"v_6_1\"}]}"; refJSON = "{\"values\":[{\"z\":0, \"t\":0, \"value\":\"v_0_0\"}, {\"z\":0, \"t\":3, \"value\":\"v_3_0\"}, {\"z\":1, \"t\":6, \"value\":\"v_6_1\"}, {\"z\":2, \"t\":3, \"value\":\"v_3_2\"}]}"; refPartlyCondensibleJSON = "{\"values\":[{\"z\":0, \"t\":0, \"value\":\"0\"}, {\"z\":1, \"t\":0, \"tmax\":1, \"value\":\"0\"}, {\"z\":1, \"t\":3, \"tmax\":5, \"value\":\"0\"}, {\"z\":1, \"t\":6, \"value\":\"otherValue\"}, {\"z\":2, \"t\":6, \"value\":\"0\"}]}"; refCondensibleJSON = "{\"values\":[{\"z\":0, \"zmax\":2, \"t\":0, \"tmax\":1, \"value\":\"1\"}]}"; refProp = mitk::TemporoSpatialStringProperty::New(); refProp->SetValue(0, 0, "v_0_0"); refProp->SetValue(3, 0, "v_3_0"); refProp->SetValue(3, 2, "v_3_2"); refProp->SetValue(6, 1, "v_6_1"); refPartlyCondensibleProp = mitk::TemporoSpatialStringProperty::New(); refPartlyCondensibleProp->SetValue(0, 0, "0"); refPartlyCondensibleProp->SetValue(0, 1, "0"); refPartlyCondensibleProp->SetValue(1, 1, "0"); refPartlyCondensibleProp->SetValue(3, 1, "0"); refPartlyCondensibleProp->SetValue(4, 1, "0"); refPartlyCondensibleProp->SetValue(5, 1, "0"); refPartlyCondensibleProp->SetValue(6, 1, "otherValue"); refPartlyCondensibleProp->SetValue(6, 2, "0"); refCondensibleProp = mitk::TemporoSpatialStringProperty::New(); refCondensibleProp->SetValue(0, 0, "1"); refCondensibleProp->SetValue(1, 0, "1"); refCondensibleProp->SetValue(0, 1, "1"); refCondensibleProp->SetValue(1, 1, "1"); refCondensibleProp->SetValue(0, 2, "1"); refCondensibleProp->SetValue(1, 2, "1"); } void tearDown() override {} void GetValue() { CPPUNIT_ASSERT(refProp->GetValue() == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 0) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 2) == "v_3_2"); CPPUNIT_ASSERT(refProp->GetValue(3, 1, false, true) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 5, false, true) == "v_3_2"); CPPUNIT_ASSERT(refProp->GetValueBySlice(0) == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValueBySlice(4, true) == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(3) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(6) == "v_6_1"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(5, true) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValueAsString() == "v_0_0"); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps().size() == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[1] == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[2] == 6); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(0).size() == 2); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(0)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(0)[1] == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(1).size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(1)[0] == 6); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(5).size() == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices().size() == 3); CPPUNIT_ASSERT(refProp->GetAvailableSlices()[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices()[1] == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices()[2] == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0).size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3).size() == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3)[1] == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(2).size() == 0); } void HasValue() { CPPUNIT_ASSERT(refProp->HasValue()); CPPUNIT_ASSERT(refProp->HasValue(3, 0)); CPPUNIT_ASSERT(refProp->HasValue(3, 2)); CPPUNIT_ASSERT(refProp->HasValue(3, 1, false, true)); CPPUNIT_ASSERT(refProp->HasValue(3, 5, false, true)); CPPUNIT_ASSERT(!refProp->HasValue(3, 1)); CPPUNIT_ASSERT(!refProp->HasValue(3, 5)); CPPUNIT_ASSERT(refProp->HasValue(4, 2, true, true)); CPPUNIT_ASSERT(refProp->HasValue(4, 2, true, false)); CPPUNIT_ASSERT(!refProp->HasValue(4, 2, false, true)); CPPUNIT_ASSERT(refProp->HasValueBySlice(0)); CPPUNIT_ASSERT(refProp->HasValueBySlice(4, true)); CPPUNIT_ASSERT(!refProp->HasValueBySlice(4)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(3)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(6)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(5, true)); CPPUNIT_ASSERT(!refProp->HasValueByTimeStep(5)); } void SetValue() { CPPUNIT_ASSERT_NO_THROW(refProp->SetValue(8, 9, "v_8_9")); CPPUNIT_ASSERT(refProp->GetValue(8, 9) == "v_8_9"); CPPUNIT_ASSERT_NO_THROW(refProp->SetValue("newValue")); CPPUNIT_ASSERT(refProp->GetValue(0, 0) == "newValue"); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps().size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0).size() == 1); } void IsUniform() { CPPUNIT_ASSERT(!refProp->IsUniform()); CPPUNIT_ASSERT(!refPartlyCondensibleProp->IsUniform()); CPPUNIT_ASSERT(refCondensibleProp->IsUniform()); } 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() { mitk::BaseProperty::Pointer prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refJSON); auto *tsProp = dynamic_cast<mitk::TemporoSpatialStringProperty *>(prop.GetPointer()); CPPUNIT_ASSERT( tsProp->GetValue(0, 0) == "v_0_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 1"); CPPUNIT_ASSERT( tsProp->GetValue(3, 0) == "v_3_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 2"); CPPUNIT_ASSERT( tsProp->GetValue(3, 2) == "v_3_2"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 3"); CPPUNIT_ASSERT( tsProp->GetValue(6, 1) == "v_6_1"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 4"); CPPUNIT_ASSERT(*tsProp == *refProp); //"Testing deserializeJSONToTemporoSpatialStringProperty()"); prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refJSON_legacy); tsProp = dynamic_cast<mitk::TemporoSpatialStringProperty*>(prop.GetPointer()); CPPUNIT_ASSERT( tsProp->GetValue(0, 0) == "v_0_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 1"); CPPUNIT_ASSERT( tsProp->GetValue(3, 0) == "v_3_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 2"); CPPUNIT_ASSERT( tsProp->GetValue(3, 2) == "v_3_2"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 3"); CPPUNIT_ASSERT( tsProp->GetValue(6, 1) == "v_6_1"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 4"); CPPUNIT_ASSERT(*tsProp == *refProp); //"Testing deserializeJSONToTemporoSpatialStringProperty()"); prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refPartlyCondensibleJSON); tsProp = dynamic_cast<mitk::TemporoSpatialStringProperty*>(prop.GetPointer()); CPPUNIT_ASSERT(tsProp->GetValue(0, 0) =="0"); CPPUNIT_ASSERT(tsProp->GetValue(0, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(1, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(3, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(4, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(5, 1) == "0"); CPPUNIT_ASSERT(tsProp->GetValue(6, 1) == "otherValue"); CPPUNIT_ASSERT(tsProp->GetValue(6, 2) == "0"); CPPUNIT_ASSERT(*tsProp == *refPartlyCondensibleProp); prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refCondensibleJSON); tsProp = dynamic_cast<mitk::TemporoSpatialStringProperty*>(prop.GetPointer()); CPPUNIT_ASSERT(tsProp->GetValue(0, 0) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(1, 0) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(0, 1) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(1, 1) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(0, 2) == "1"); CPPUNIT_ASSERT(tsProp->GetValue(1, 2) == "1"); CPPUNIT_ASSERT(*tsProp == *refCondensibleProp); } }; MITK_TEST_SUITE_REGISTRATION(mitkTemporoSpatialStringProperty) diff --git a/Modules/DataTypesExt/include/mitkBoundingObject.h b/Modules/DataTypesExt/include/mitkBoundingObject.h index 9a7c6128a7..9487df2037 100644 --- a/Modules/DataTypesExt/include/mitkBoundingObject.h +++ b/Modules/DataTypesExt/include/mitkBoundingObject.h @@ -1,64 +1,62 @@ /*============================================================================ 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 mitkBoundingObject_h #define mitkBoundingObject_h #include "MitkDataTypesExtExports.h" #include <mitkSurface.h> namespace mitk { //##Documentation //## @brief superclass of all bounding objects (cylinder, cuboid,...) //## //## Manages generic functions and provides an interface for IsInside() //## calculates a generic bounding box //## @ingroup Data class MITKDATATYPESEXT_EXPORT BoundingObject : public mitk::Surface // BaseData { public: mitkClassMacro(BoundingObject, mitk::Surface); virtual bool IsInside(const mitk::Point3D &p) const = 0; virtual mitk::ScalarType GetVolume(); itkGetMacro(Positive, bool); itkSetMacro(Positive, bool); itkBooleanMacro(Positive); //##Documentation //## @brief Sets the Geometry3D of the bounding object to fit the given //## geometry. //## //## The fit is done once, so if the given geometry changes it will //## \em not effect the bounding object. virtual void FitGeometry(BaseGeometry *aGeometry3D); protected: BoundingObject(); ~BoundingObject() override; - bool WriteXMLData(XMLWriter &xmlWriter); - //##Documentation //## \brief If \a true, the Boundingobject describes a positive volume, //## if \a false a negative volume. //## bool m_Positive; private: BoundingObject(const BoundingObject &); BoundingObject &operator=(const BoundingObject &); }; } #endif diff --git a/Modules/ModelFit/include/mitkScalarListLookupTable.h b/Modules/ModelFit/include/mitkScalarListLookupTable.h index 92c1541736..b7bed70910 100644 --- a/Modules/ModelFit/include/mitkScalarListLookupTable.h +++ b/Modules/ModelFit/include/mitkScalarListLookupTable.h @@ -1,91 +1,95 @@ /*============================================================================ 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 mitkScalarListLookupTable_h #define mitkScalarListLookupTable_h #include <string> #include <vector> #include <map> +#include <nlohmann/json_fwd.hpp> #include "MitkModelFitExports.h" namespace mitk { /** * @brief Data class for modelfit properties that store a map of lists (e.g. static * parameters). */ class MITKMODELFIT_EXPORT ScalarListLookupTable { public: typedef std::string KeyType; typedef std::vector<double> ValueType; typedef std::map<KeyType, ValueType> LookupTableType; typedef std::pair<KeyType, ValueType> EntryType; ScalarListLookupTable() {} virtual ~ScalarListLookupTable() {} virtual const char* GetNameOfClass() const; /** * @brief Sets the list at the given map key to the given value. * @param key The name of the list (i.e. map key). * @param value The list to be added (i.e. map value). */ void SetTableValue(const KeyType& key, const ValueType& value); /** * @brief Returns true if the list with the given name exists. * @param key The name of the desired list (i.e. map key) * @return true if the list exists or false otherwise. */ bool ValueExists(const KeyType& key) const; /** * @brief Returns the list with the given name. * @param key The name (i.e. map key) of the desired list. * @return The list with the given name. * @throw std::range_error If the list doesn't exist. */ const ValueType& GetTableValue(const KeyType& key) const; /** * @brief Returns the map of lists. * @return The map of lists. */ const LookupTableType& GetLookupTable() const; void SetLookupTable(const LookupTableType& table); bool operator==(const ScalarListLookupTable& lookupTable) const; bool operator!=(const ScalarListLookupTable& lookupTable) const; virtual ScalarListLookupTable& operator=(const ScalarListLookupTable& other); protected: LookupTableType m_LookupTable; }; /** * @brief Adds the string representation of the given ScalarListLookupTable to the * given stream. * @param stream The stream to which the map's string representation should be added. * @param l The map whose string representation should be added to the stream. * @return The given stream with the added string representation of the map. */ 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 35d1c336ac..fa8c3b5a3b 100644 --- a/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp +++ b/Modules/ModelFit/src/Common/mitkScalarListLookupTable.cpp @@ -1,116 +1,131 @@ /*============================================================================ 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 "mitkScalarListLookupTable.h" #include <stdexcept> #include <iostream> +#include <nlohmann/json.hpp> + +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<ScalarListLookupTable::LookupTableType>()); + } +} + const char* mitk::ScalarListLookupTable::GetNameOfClass() const { return "ScalarListLookupTable"; } void mitk::ScalarListLookupTable::SetTableValue(const KeyType& key, const ValueType& value) { m_LookupTable[key] = value; } bool mitk::ScalarListLookupTable::ValueExists(const KeyType& key) const { LookupTableType::const_iterator it = m_LookupTable.find(key); return (it != m_LookupTable.end()); } const mitk::ScalarListLookupTable::ValueType& mitk::ScalarListLookupTable::GetTableValue(const KeyType& key) const { LookupTableType::const_iterator it = m_LookupTable.find(key); if (it != m_LookupTable.end()) { return it->second; } else { throw std::range_error("id does not exist in the lookup table"); } } const mitk::ScalarListLookupTable::LookupTableType& mitk::ScalarListLookupTable::GetLookupTable() const { return m_LookupTable; } void mitk::ScalarListLookupTable::SetLookupTable(const LookupTableType& table) { m_LookupTable = table; }; bool mitk::ScalarListLookupTable::operator==(const mitk::ScalarListLookupTable& lookupTable) const { return (m_LookupTable == lookupTable.m_LookupTable); } bool mitk::ScalarListLookupTable::operator!=(const mitk::ScalarListLookupTable& lookupTable) const { return !(m_LookupTable == lookupTable.m_LookupTable); } mitk::ScalarListLookupTable& mitk::ScalarListLookupTable::operator=(const ScalarListLookupTable& other) { if (this == &other) { return *this; } else { m_LookupTable = other.m_LookupTable; return *this; } } std::ostream& mitk::operator<<(std::ostream& stream, const ScalarListLookupTable& l) { typedef ScalarListLookupTable::LookupTableType::const_iterator MapIterType; typedef ScalarListLookupTable::ValueType::const_iterator VectorIterType; MapIterType mapStart = l.GetLookupTable().begin(); MapIterType mapEnd = l.GetLookupTable().end(); stream << "["; for (MapIterType i = mapStart; i != mapEnd; ++i) { if (i != mapStart) { stream << ", "; } stream << i->first << " -> ["; VectorIterType vectorStart = i->second.begin(); VectorIterType vectorEnd = i->second.end(); for (VectorIterType j = vectorStart; j != vectorEnd; ++j) { if (j != vectorStart) { stream << ", "; } stream << *j; } stream << "]"; } return stream << "]"; }; diff --git a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp index 4793618497..692892d094 100644 --- a/Modules/ModelFit/test/mitkModelFitInfoTest.cpp +++ b/Modules/ModelFit/test/mitkModelFitInfoTest.cpp @@ -1,424 +1,425 @@ /*============================================================================ 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 <iostream> #include "mitkProperties.h" #include "mitkStandaloneDataStorage.h" #include "mitkModelFitInfo.h" #include "mitkModelFitConstants.h" #include "mitkModelFitException.h" #include "mitkModelFitResultRelationRule.h" #include <mitkTestFixture.h> #include <mitkTestingMacros.h> +#include <mitkUIDGenerator.h> mitk::modelFit::ModelFitInfo::UIDType ensureModelFitUID(mitk::BaseData * data) { mitk::BaseProperty::Pointer uidProp = data->GetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str()); std::string propUID = ""; if (uidProp.IsNotNull()) { propUID = uidProp->GetValueAsString(); } else { mitk::UIDGenerator generator; propUID = generator.GetUID(); data->SetProperty(mitk::ModelFitConstants::LEGACY_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(propUID)); } return propUID; }; mitk::DataNode::Pointer generateModelFitTestNode() { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetName("Param1"); auto testImage = mitk::Image::New(); node->SetData(testImage); testImage->SetProperty("modelfit.testEmpty", mitk::StringProperty::New("")); testImage->SetProperty("modelfit.testValid", mitk::StringProperty::New("test")); ensureModelFitUID(testImage); testImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy")); testImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName")); testImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); testImage->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("input UID")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); testImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); testImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); testImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); testImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); testImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); return node; } class mitkModelFitInfoTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkModelFitInfoTestSuite); MITK_TEST(CheckModelFitInfo); MITK_TEST(CheckGetMandatoryProperty); MITK_TEST(CheckCreateFitInfoFromNode_Legacy); MITK_TEST(CheckCreateFitInfoFromNode); MITK_TEST(CheckGetNodesOfFit); MITK_TEST(CheckGetFitUIDsOfNode); CPPUNIT_TEST_SUITE_END(); mitk::StandaloneDataStorage::Pointer m_Storage; mitk::Image::Pointer m_ParamImage; mitk::Image::Pointer m_ParamImage2; mitk::Image::Pointer m_ParamImage_legacy; mitk::Image::Pointer m_ParamImage2_legacy; mitk::DataNode::Pointer m_ParamImageNode; mitk::DataNode::Pointer m_ParamImageNode2; mitk::DataNode::Pointer m_ParamImageNode_legacy; mitk::DataNode::Pointer m_ParamImageNode2_legacy; public: void setUp() override { m_Storage = mitk::StandaloneDataStorage::New(); //create input node mitk::DataNode::Pointer inputNode = mitk::DataNode::New(); inputNode->SetName("Input"); mitk::Image::Pointer image = mitk::Image::New(); inputNode->SetData(image); mitk::modelFit::ModelFitInfo::UIDType inputUID = ensureModelFitUID(image); m_Storage->Add(inputNode); mitk::DataStorage::SetOfObjects::Pointer parents = mitk::DataStorage::SetOfObjects::New(); parents->push_back(inputNode); ///////////////////////////////////////////////////// //Create nodes for a fit (new style using rules) ///////////////////////////////////////////////////// auto rule = mitk::ModelFitResultRelationRule::New(); //create first result for FitLegacy m_ParamImageNode = mitk::DataNode::New(); m_ParamImage = mitk::Image::New(); m_ParamImageNode->SetData(m_ParamImage); m_ParamImageNode->SetName("Param1"); m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit")); m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); m_ParamImage->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); m_ParamImage->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1")); m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b")); m_ParamImage->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str())); rule->Connect(m_ParamImage, image); m_Storage->Add(m_ParamImageNode, parents); //create second result for Fit m_ParamImageNode2 = mitk::DataNode::New(); m_ParamImageNode2->SetName("Param2"); m_ParamImage2 = mitk::Image::New(); m_ParamImageNode2->SetData(m_ParamImage2); m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage2->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage2->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); m_ParamImage2->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str())); rule->Connect(m_ParamImage2, image); m_Storage->Add(m_ParamImageNode2, parents); ///////////////////////////////////////////////////// //Create nodes for a fit in legacy mode (old fit uid) ///////////////////////////////////////////////////// //create first result for FitLegacy m_ParamImageNode_legacy = mitk::DataNode::New(); m_ParamImage_legacy = mitk::Image::New(); m_ParamImageNode_legacy->SetData(m_ParamImage_legacy); m_ParamImageNode_legacy->SetName("Param1_legacy"); ensureModelFitUID(m_ParamImage_legacy); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str())); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param1_legacy")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("b")); m_ParamImage_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_PARAMETER().c_str())); m_Storage->Add(m_ParamImageNode_legacy, parents); //create second result for FitLegacy m_ParamImageNode2_legacy = mitk::DataNode::New(); m_ParamImageNode2_legacy->SetName("Param2_legacy"); m_ParamImage2_legacy = mitk::Image::New(); m_ParamImageNode2_legacy->SetData(m_ParamImage2_legacy); ensureModelFitUID(m_ParamImage2_legacy); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("FitLegacy")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName1")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::LEGACY_FIT_INPUT_IMAGEUID_PROPERTY_NAME().c_str(), mitk::StringProperty::New(inputUID.c_str())); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_1")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::MODEL_X_PROPERTY_NAME().c_str(), mitk::StringProperty::New("myX")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::XAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("h")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT().c_str())); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::YAXIS_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("kg")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param2_legacy")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); m_ParamImage2_legacy->SetProperty(mitk::ModelFitConstants::PARAMETER_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::PARAMETER_TYPE_VALUE_DERIVED_PARAMETER().c_str())); m_Storage->Add(m_ParamImageNode2_legacy, parents); ///////////////////////////////////////////////////// //Create nodes for a fit on other input auto anotherInputNode = mitk::DataNode::New(); anotherInputNode->SetName("AnotherInput"); auto anotherImage = mitk::Image::New(); anotherInputNode->SetData(anotherImage); m_Storage->Add(anotherInputNode); parents = mitk::DataStorage::SetOfObjects::New(); parents->push_back(anotherInputNode); mitk::DataNode::Pointer node3 = mitk::DataNode::New(); node3->SetName("Param_Other"); mitk::Image::Pointer paramImage3 = mitk::Image::New(); node3->SetData(paramImage3); paramImage3->SetProperty(mitk::ModelFitConstants::FIT_UID_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Fit2")); paramImage3->SetProperty(mitk::ModelFitConstants::FIT_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("MyName2")); paramImage3->SetProperty(mitk::ModelFitConstants::FIT_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New(mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED().c_str())); paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_TYPE_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModels")); paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("TestModel_2")); paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_PROPERTY_NAME().c_str(), mitk::StringProperty::New("")); paramImage3->SetProperty(mitk::ModelFitConstants::MODEL_FUNCTION_CLASS_PROPERTY_NAME().c_str(), mitk::StringProperty::New("ModelClass_B")); paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), mitk::StringProperty::New("Param_Other")); paramImage3->SetProperty(mitk::ModelFitConstants::PARAMETER_UNIT_PROPERTY_NAME().c_str(), mitk::StringProperty::New("a")); rule->Connect(paramImage3, anotherImage); m_Storage->Add(node3, parents); } void tearDown() override { } void CheckModelFitInfo() { mitk::modelFit::Parameter::Pointer p = mitk::modelFit::Parameter::New(); p->name = "p"; mitk::modelFit::ModelFitInfo::Pointer fit = mitk::modelFit::ModelFitInfo::New(); fit->AddParameter(p); CPPUNIT_ASSERT_MESSAGE("AddParameter unsuccessfully adds a parameter.", fit->GetParameters().size() == 1); mitk::modelFit::Parameter::ConstPointer resultParam = fit->GetParameter("test", mitk::modelFit::Parameter::ParameterType); CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns NULL for wrong parameter.", resultParam.IsNull()); resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::ParameterType); CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter.", resultParam == p); p->type = mitk::modelFit::Parameter::CriterionType; resultParam = fit->GetParameter("p", mitk::modelFit::Parameter::CriterionType); CPPUNIT_ASSERT_MESSAGE("Testing if GetParameter returns the correct parameter with a non-default type.", resultParam == p); fit->DeleteParameter("test", mitk::modelFit::Parameter::CriterionType); CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter fails for wrong parameter.", fit->GetParameters().size() == 1); fit->DeleteParameter("p", mitk::modelFit::Parameter::CriterionType); CPPUNIT_ASSERT_MESSAGE("Testing if DeleteParameter successfully removes a parameter.", fit->GetParameters().size() == 0); } void CheckGetMandatoryProperty() { mitk::DataNode::Pointer node = generateModelFitTestNode(); mitk::DataNode::Pointer invalideNode = mitk::DataNode::New(); CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testInvalid"), mitk::modelFit::ModelFitException); CPPUNIT_ASSERT_THROW(mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testEmpty"), mitk::modelFit::ModelFitException); CPPUNIT_ASSERT_MESSAGE("Testing if GetMandatoryProperty returns the correct value.", mitk::modelFit::GetMandatoryProperty(node.GetPointer(), "modelfit.testValid") == "test"); } void CheckCreateFitInfoFromNode_Legacy() { CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("FitLegacy", nullptr).IsNull()); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull()); mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("FitLegacy", m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.", resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() && resultFit->uid == "FitLegacy" && resultFit->fitName == "MyName1" && resultFit->modelType == "TestModels" && resultFit->modelName == "TestModel_1" && resultFit->function == "" && resultFit->functionClassID == "ModelClass" && resultFit->x == "myX" && resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() && resultFit->xAxisUnit == "h" && resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() && resultFit->yAxisUnit == "kg" && resultFit->GetParameters().size() == 2); mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1_legacy", mitk::modelFit::Parameter::ParameterType); CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1_legacy" && param->unit == "b" && param->image == m_ParamImage_legacy); mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2_legacy", mitk::modelFit::Parameter::DerivedType); CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2_legacy" && param2->unit == "a" && param2->image == m_ParamImage2_legacy); } void CheckCreateFitInfoFromNode() { CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for invalid node.", mitk::modelFit::CreateFitInfoFromNode("Fit", nullptr).IsNull()); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns NULL for node with missing properties.", mitk::modelFit::CreateFitInfoFromNode("invalide_UID", m_Storage).IsNull()); mitk::modelFit::ModelFitInfo::Pointer resultFit = mitk::modelFit::CreateFitInfoFromNode("Fit", m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode returns a valid model fit info.", resultFit.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if CreateFitInfoFromNode creates a fit with correct attributes.", resultFit->fitType == mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED() && resultFit->uid == "Fit" && resultFit->fitName == "MyName1" && resultFit->modelType == "TestModels" && resultFit->modelName == "TestModel_1" && resultFit->function == "" && resultFit->functionClassID == "ModelClass" && resultFit->x == "myX" && resultFit->xAxisName == mitk::ModelFitConstants::XAXIS_NAME_VALUE_DEFAULT() && resultFit->xAxisUnit == "h" && resultFit->yAxisName == mitk::ModelFitConstants::YAXIS_NAME_VALUE_DEFAULT() && resultFit->yAxisUnit == "kg" && resultFit->GetParameters().size() == 2); mitk::modelFit::Parameter::ConstPointer param = resultFit->GetParameter("Param1", mitk::modelFit::Parameter::ParameterType); CPPUNIT_ASSERT_MESSAGE("Testing if param 1 exists.", param.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if param 1 is configured correctly.", param->name == "Param1" && param->unit == "b" && param->image == m_ParamImage); mitk::modelFit::Parameter::ConstPointer param2 = resultFit->GetParameter("Param2", mitk::modelFit::Parameter::DerivedType); CPPUNIT_ASSERT_MESSAGE("Testing if param 2 exists.", param2.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Testing if param 2 is configured correctly.", param2->name == "Param2" && param2->unit == "a" && param2->image == m_ParamImage2); } void CheckGetNodesOfFit() { auto nodes = mitk::modelFit::GetNodesOfFit("Fit", m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit", nodes->Size() == 2); CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode.GetPointer()) != nodes->end()); CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2.GetPointer()) != nodes->end()); nodes = mitk::modelFit::GetNodesOfFit("FitLegacy", m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for FitLegacy", nodes->Size() == 2); CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode_legacy.GetPointer()) != nodes->end()); CPPUNIT_ASSERT(std::find(nodes->begin(), nodes->end(), m_ParamImageNode2_legacy.GetPointer()) != nodes->end()); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for Fit2", mitk::modelFit::GetNodesOfFit("Fit2", m_Storage)->Size() == 1); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for unkown fits.", mitk::modelFit::GetNodesOfFit("unkown_fit", m_Storage)->Size() == 0); CPPUNIT_ASSERT_MESSAGE("Testing if GetNodesOfFit works correctly for illegal calls.", mitk::modelFit::GetNodesOfFit("unkown_fit", nullptr).IsNull()); } void CheckGetFitUIDsOfNode() { auto testNode = m_Storage->GetNamedNode("Input"); mitk::modelFit::NodeUIDSetType uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.", uidSet.size() == 2 && uidSet.find("Fit") != uidSet.end() && uidSet.find("FitLegacy") != uidSet.end()); testNode = m_Storage->GetNamedNode("AnotherInput"); uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly.", uidSet.size() == 1 && uidSet.find("Fit2") != uidSet.end()); uidSet = mitk::modelFit::GetFitUIDsOfNode(nullptr, m_Storage); CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid node.", uidSet.size() == 0); uidSet = mitk::modelFit::GetFitUIDsOfNode(testNode, nullptr); CPPUNIT_ASSERT_MESSAGE("Testing if GetFitUIDsOfNode works correctly with invalid storage.", uidSet.size() == 0); } }; MITK_TEST_SUITE_REGISTRATION(mitkModelFitInfo) \ No newline at end of file diff --git a/Modules/ModuleList.cmake b/Modules/ModuleList.cmake index 89d0588465..b027952284 100644 --- a/Modules/ModuleList.cmake +++ b/Modules/ModuleList.cmake @@ -1,77 +1,78 @@ # The entries in the mitk_modules list must be # ordered according to their dependencies. set(MITK_MODULES Log Core CommandLine CoreCmdApps AppUtil LegacyIO DataTypesExt Annotation LegacyGL AlgorithmsExt MapperExt DICOM DICOMQI DICOMTesting SceneSerializationBase PlanarFigure ImageDenoising ImageExtraction SceneSerialization Gizmo GraphAlgorithms Multilabel Chart ImageStatistics ContourModel SurfaceInterpolation Segmentation QtWidgets QtWidgetsExt ImageStatisticsUI SegmentationUI MatchPointRegistration MatchPointRegistrationUI Classification OpenIGTLink IGTBase IGT CameraCalibration OpenCL OpenCVVideoSupport QtOverlays ToFHardware ToFProcessing ToFUI US USUI DICOMUI Remeshing Python QtPython Persistence OpenIGTLinkUI IGTUI RT RTUI IOExt XNAT TubeGraph BoundingShape RenderWindowManagerUI SemanticRelations SemanticRelationsUI CEST BasicImageProcessing ModelFit ModelFitUI Pharmacokinetics PharmacokineticsUI DICOMPM REST RESTService DICOMweb + ROI ) diff --git a/Modules/Multilabel/mitkLabel.h b/Modules/Multilabel/mitkLabel.h index 0d64a124df..968452be5d 100644 --- a/Modules/Multilabel/mitkLabel.h +++ b/Modules/Multilabel/mitkLabel.h @@ -1,106 +1,107 @@ /*============================================================================ 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 mitkLabel_h #define mitkLabel_h #include "MitkMultilabelExports.h" #include <mitkColorProperty.h> #include <mitkPropertyList.h> +#include <mitkPoint.h> #include <mitkVector.h> namespace mitk { //## //##Documentation //## @brief A data structure describing a label. //## @ingroup Data //## class MITKMULTILABEL_EXPORT Label : public PropertyList { public: mitkClassMacro(Label, mitk::PropertyList); typedef unsigned short PixelType; itkNewMacro(Self); mitkNewMacro2Param(Self, PixelType, const std::string&); /// The maximum value a label can get: Since the value is of type unsigned short MAX_LABEL_VALUE = 65535 static const PixelType MAX_LABEL_VALUE; void SetLocked(bool locked); bool GetLocked() const; void SetVisible(bool visible); bool GetVisible() const; void SetOpacity(float opacity); float GetOpacity() const; void SetName(const std::string &name); std::string GetName() const; void SetCenterOfMassIndex(const mitk::Point3D ¢er); mitk::Point3D GetCenterOfMassIndex() const; void SetCenterOfMassCoordinates(const mitk::Point3D ¢er); mitk::Point3D GetCenterOfMassCoordinates() const; void SetColor(const mitk::Color &); const mitk::Color &GetColor() const; void SetValue(PixelType pixelValue); PixelType GetValue() const; void SetLayer(unsigned int layer); unsigned int GetLayer() const; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; using itk::Object::Modified; void Modified() { Superclass::Modified(); } Label(); Label(PixelType value, const std::string& name); ~Label() override; protected: void PrintSelf(std::ostream &os, itk::Indent indent) const override; Label(const Label &other); private: itk::LightObject::Pointer InternalClone() const override; }; /** * @brief Equal A function comparing two labels for beeing equal in data * * @ingroup MITKTestingAPI * * Following aspects are tested for equality: * - Lebel equality via Equal-PropetyList * * @param rightHandSide An image to be compared * @param leftHandSide An image to be compared * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return true, if all subsequent comparisons are true, false otherwise */ MITKMULTILABEL_EXPORT bool Equal(const mitk::Label &leftHandSide, const mitk::Label &rightHandSide, ScalarType eps, bool verbose); } // namespace mitk #endif diff --git a/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp b/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp index f6e48f7dcc..40ad9545b1 100644 --- a/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp +++ b/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp @@ -1,308 +1,307 @@ /*============================================================================ 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 "mitkIGTLDeviceSource.h" #include "mitkIGTLDevice.h" #include "mitkIGTLMessage.h" -//#include "mitkIGTTimeStamp.h" -//#include "mitkIGTException.h" +#include <mitkUIDGenerator.h> //Microservices #include <usGetModuleContext.h> #include <usModule.h> #include <usServiceProperties.h> #include <usModuleContext.h> //itk #include <itkCommand.h> const std::string mitk::IGTLDeviceSource::US_PROPKEY_IGTLDEVICENAME = mitk::IGTLMessageSource::US_INTERFACE_NAME + ".igtldevicename"; mitk::IGTLDeviceSource::IGTLDeviceSource() : mitk::IGTLMessageSource(), m_IGTLDevice(nullptr) { this->SetName("IGTLDeviceSource (no defined type)"); } mitk::IGTLDeviceSource::~IGTLDeviceSource() { if (m_IGTLDevice.IsNotNull()) { if (m_IGTLDevice->GetState() == mitk::IGTLDevice::Running) { this->StopCommunication(); } if (m_IGTLDevice->GetState() == mitk::IGTLDevice::Ready) { this->Disconnect(); } this->RemoveObservers(); m_IGTLDevice = nullptr; } } void mitk::IGTLDeviceSource::GenerateData() { if (m_IGTLDevice.IsNull()) return; /* update output with message from the device */ IGTLMessage* msgOut = this->GetOutput(); assert(msgOut); igtl::MessageBase::Pointer msgIn = dynamic_cast<igtl::MessageBase*>(m_IGTLDevice->GetNextImage2dMessage().GetPointer()); if (msgIn.IsNotNull()) { assert(msgIn); msgOut->SetMessage(msgIn); msgOut->SetName(msgIn->GetDeviceName()); } // else // { // MITK_ERROR("IGTLDeviceSource") << "Could not get the latest message."; // } } void mitk::IGTLDeviceSource::RemoveObservers() { if (this->m_IGTLDevice.IsNotNull()) { this->m_IGTLDevice->RemoveObserver(m_IncomingMessageObserverTag); this->m_IGTLDevice->RemoveObserver(m_IncomingCommandObserverTag); this->m_IGTLDevice->RemoveObserver(m_LostConnectionObserverTag); } } void mitk::IGTLDeviceSource::SetIGTLDevice(mitk::IGTLDevice* igtlDevice) { MITK_DEBUG << "Setting IGTLDevice to " << igtlDevice; if (this->m_IGTLDevice.GetPointer() != igtlDevice) { //check if we want to override the device if (this->m_IGTLDevice.IsNotNull()) { //the device was set previously => we need to reset the observers this->RemoveObservers(); } //set the device this->m_IGTLDevice = igtlDevice; this->CreateOutputs(); std::stringstream name; // create a human readable name for the source name << "OIGTL Device Source ( " << igtlDevice->GetName() << " )"; this->SetName(name.str()); //setup a observer that listens to new messages and new commands typedef itk::SimpleMemberCommand<IGTLDeviceSource> DeviceSrcCommand; DeviceSrcCommand::Pointer msgReceivedCommand = DeviceSrcCommand::New(); msgReceivedCommand->SetCallbackFunction(this, &IGTLDeviceSource::OnIncomingMessage); this->m_IncomingMessageObserverTag = this->m_IGTLDevice->AddObserver(mitk::MessageReceivedEvent(), msgReceivedCommand); DeviceSrcCommand::Pointer cmdReceivedCommand = DeviceSrcCommand::New(); cmdReceivedCommand->SetCallbackFunction(this, &IGTLDeviceSource::OnIncomingCommand); this->m_IncomingCommandObserverTag = this->m_IGTLDevice->AddObserver(mitk::CommandReceivedEvent(), cmdReceivedCommand); DeviceSrcCommand::Pointer connectionLostCommand = DeviceSrcCommand::New(); connectionLostCommand->SetCallbackFunction(this, &IGTLDeviceSource::OnLostConnection); this->m_LostConnectionObserverTag = this->m_IGTLDevice->AddObserver(mitk::LostConnectionEvent(), connectionLostCommand); } } void mitk::IGTLDeviceSource::CreateOutputs() { //if outputs are set then delete them if (this->GetNumberOfOutputs() > 0) { for (int numOP = this->GetNumberOfOutputs() - 1; numOP >= 0; numOP--) this->RemoveOutput(numOP); this->Modified(); } //fill the outputs if a valid OpenIGTLink device is set if (m_IGTLDevice.IsNull()) return; this->SetNumberOfIndexedOutputs(1); if (this->GetOutput(0) == nullptr) { DataObjectPointer newOutput = this->MakeOutput(0); this->SetNthOutput(0, newOutput); this->Modified(); } } void mitk::IGTLDeviceSource::Connect() { if (m_IGTLDevice.IsNull()) { throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); } if (this->IsConnected()) { return; } try { m_IGTLDevice->OpenConnection(); } catch (mitk::Exception &e) { throw std::runtime_error(std::string("mitk::IGTLDeviceSource: Could not open" "connection to OpenIGTLink device. Error: ") + e.GetDescription()); } } void mitk::IGTLDeviceSource::StartCommunication() { if (m_IGTLDevice.IsNull()) throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); if (m_IGTLDevice->GetState() == mitk::IGTLDevice::Running) return; if (m_IGTLDevice->StartCommunication() == false) throw std::runtime_error("mitk::IGTLDeviceSource: " "Could not start communication"); } void mitk::IGTLDeviceSource::Disconnect() { if (m_IGTLDevice.IsNull()) throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); if (m_IGTLDevice->CloseConnection() == false) throw std::runtime_error("mitk::IGTLDeviceSource: Could not close connection" " to OpenIGTLink device"); } void mitk::IGTLDeviceSource::StopCommunication() { if (m_IGTLDevice.IsNull()) throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); if (m_IGTLDevice->StopCommunication() == false) throw std::runtime_error("mitk::IGTLDeviceSource: " "Could not stop communicating"); } void mitk::IGTLDeviceSource::UpdateOutputInformation() { this->Modified(); // make sure that we need to be updated Superclass::UpdateOutputInformation(); } void mitk::IGTLDeviceSource::SetInput(unsigned int idx, const IGTLMessage* msg) { if (msg == nullptr) // if an input is set to nullptr, remove it { this->RemoveInput(idx); } else { // ProcessObject is not const-correct so a const_cast is required here this->ProcessObject::SetNthInput(idx, const_cast<IGTLMessage*>(msg)); } // this->CreateOutputsForAllInputs(); } bool mitk::IGTLDeviceSource::IsConnected() { if (m_IGTLDevice.IsNull()) return false; return (m_IGTLDevice->GetState() == mitk::IGTLDevice::Ready) || (m_IGTLDevice->GetState() == mitk::IGTLDevice::Running); } bool mitk::IGTLDeviceSource::IsCommunicating() { if (m_IGTLDevice.IsNull()) return false; return m_IGTLDevice->GetState() == mitk::IGTLDevice::Running; } void mitk::IGTLDeviceSource::RegisterAsMicroservice() { // Get Context us::ModuleContext* context = us::GetModuleContext(); // Define ServiceProps us::ServiceProperties props; mitk::UIDGenerator uidGen = mitk::UIDGenerator("org.mitk.services.IGTLDeviceSource.id_"); props[US_PROPKEY_ID] = uidGen.GetUID(); props[US_PROPKEY_DEVICENAME] = this->GetName(); props[US_PROPKEY_IGTLDEVICENAME] = m_Name; props[US_PROPKEY_DEVICETYPE] = m_Type; m_ServiceRegistration = context->RegisterService(this, props); MITK_INFO << "Registered new DeviceSource as microservice: " << uidGen.GetUID(); } void mitk::IGTLDeviceSource::OnIncomingMessage() { } void mitk::IGTLDeviceSource::OnIncomingCommand() { } void mitk::IGTLDeviceSource::OnLostConnection() { } const mitk::IGTLMessage* mitk::IGTLDeviceSource::GetInput(void) const { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast<const IGTLMessage*>(this->ProcessObject::GetInput(0)); } const mitk::IGTLMessage* mitk::IGTLDeviceSource::GetInput(unsigned int idx) const { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast<const IGTLMessage*>(this->ProcessObject::GetInput(idx)); } const mitk::IGTLMessage* mitk::IGTLDeviceSource::GetInput(std::string msgName) const { const DataObjectPointerArray& inputs = const_cast<Self*>(this)->GetInputs(); for (DataObjectPointerArray::const_iterator it = inputs.begin(); it != inputs.end(); ++it) if (std::string(msgName) == (static_cast<IGTLMessage*>(it->GetPointer()))->GetName()) return static_cast<IGTLMessage*>(it->GetPointer()); return nullptr; } itk::ProcessObject::DataObjectPointerArraySizeType mitk::IGTLDeviceSource::GetInputIndex(std::string msgName) { DataObjectPointerArray outputs = this->GetInputs(); for (DataObjectPointerArray::size_type i = 0; i < outputs.size(); ++i) if (msgName == (static_cast<IGTLMessage*>(outputs.at(i).GetPointer()))->GetName()) return i; throw std::invalid_argument("output name does not exist"); } diff --git a/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp b/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp index d58ba5cf27..f14ab932c2 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp +++ b/Modules/Python/autoload/PythonService/mitkPythonActivator.cpp @@ -1,63 +1,63 @@ /*============================================================================ 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 mitkPythonActivator_h #define mitkPythonActivator_h // Microservices +#include "mitkPythonService.h" #include <usModuleActivator.h> #include "usModuleContext.h" -#include "mitkPythonService.h" #include <usServiceRegistration.h> namespace mitk { /// /// installs the PythonService /// runs all initial commands (setting env paths etc) /// class PythonActivator : public us::ModuleActivator { public: void Load(us::ModuleContext* context) override { MITK_DEBUG << "PythonActivator::Load"; // Registering PythonService as MicroService m_PythonService = itk::SmartPointer<mitk::PythonService>(new PythonService()); us::ServiceProperties _PythonServiceProps; _PythonServiceProps["Name"] = std::string("PythonService"); m_PythonServiceRegistration = context->RegisterService<mitk::IPythonService>(m_PythonService.GetPointer(), _PythonServiceProps); } void Unload(us::ModuleContext*) override { MITK_DEBUG("PythonActivator") << "PythonActivator::Unload"; MITK_DEBUG("PythonActivator") << "m_PythonService GetReferenceCount " << m_PythonService->GetReferenceCount(); m_PythonServiceRegistration.Unregister(); m_PythonService->Delete(); MITK_DEBUG("PythonActivator") << "m_PythonService GetReferenceCount " << m_PythonService->GetReferenceCount(); } ~PythonActivator() override { } private: itk::SmartPointer<mitk::PythonService> m_PythonService; us::ServiceRegistration<PythonService> m_PythonServiceRegistration; }; } US_EXPORT_MODULE_ACTIVATOR(mitk::PythonActivator) #endif diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.h b/Modules/Python/autoload/PythonService/mitkPythonService.h index 31338dd8e1..c98f71a64c 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonService.h +++ b/Modules/Python/autoload/PythonService/mitkPythonService.h @@ -1,107 +1,111 @@ /*============================================================================ 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 mitkPythonService_h #define mitkPythonService_h #include <ctkAbstractPythonManager.h> -#include "mitkIPythonService.h" +#ifdef snprintf +#undef snprintf +#endif + +#include <mitkIPythonService.h> +#include <mitkSurface.h> #include <itkLightObject.h> -#include "mitkSurface.h" namespace mitk { /// /// implementation of the IPythonService using ctkabstractpythonmanager /// \see IPythonService class PythonService: public itk::LightObject, public mitk::IPythonService { public: /// /// instantiate python manager here PythonService(); /// /// empty implementation... ~PythonService() override; /// /// \see IPythonService::Execute() std::string Execute( const std::string& pythonCommand, int commandType = SINGLE_LINE_COMMAND ) override; /// /// \see IPythonService::ExecuteScript() void ExecuteScript(const std::string &pathToPythonScript) override; /// /// \see IPythonService::PythonErrorOccured() bool PythonErrorOccured() const override; /// /// \see IPythonService::GetVariableStack() std::vector<PythonVariable> GetVariableStack() const override; /// /// \see IPythonService::DoesVariableExist() bool DoesVariableExist(const std::string& name) const override; /// /// \see IPythonService::GetVariable() std::string GetVariable(const std::string& name) const override; /// /// \see IPythonService::AddPythonCommandObserver() void AddPythonCommandObserver( PythonCommandObserver* observer ) override; /// /// \see IPythonService::RemovePythonCommandObserver() void RemovePythonCommandObserver( PythonCommandObserver* observer ) override; /// /// \see IPythonService::NotifyObserver() void NotifyObserver( const std::string& command ) override; /// /// \see IPythonService::IsItkPythonWrappingAvailable() bool IsSimpleItkPythonWrappingAvailable() override; /// /// \see IPythonService::CopyToPythonAsItkImage() bool CopyToPythonAsSimpleItkImage( mitk::Image* image, const std::string& varName ) override; /// /// \see IPythonService::CopyItkImageFromPython() mitk::Image::Pointer CopySimpleItkImageFromPython( const std::string& varName ) override; /// /// \see IPythonService::IsOpenCvPythonWrappingAvailable() bool IsOpenCvPythonWrappingAvailable() override; /// /// \see IPythonService::CopyToPythonAsCvImage() bool CopyToPythonAsCvImage( mitk::Image* image, const std::string& varName ) override; /// /// \see IPythonService::CopyCvImageFromPython() mitk::Image::Pointer CopyCvImageFromPython( const std::string& varName ) override; /// /// \see IPythonService::IsVtkPythonWrappingAvailable() bool IsVtkPythonWrappingAvailable() override; /// /// \see IPythonService::CopyToPythonAsVtkPolyData() bool CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& varName ) override; /// /// \see IPythonService::CopyVtkPolyDataFromPython() mitk::Surface::Pointer CopyVtkPolyDataFromPython( const std::string& varName ) override; /// /// \return the ctk abstract python manager instance ctkAbstractPythonManager* GetPythonManager() override; void AddRelativeSearchDirs(std::vector< std::string > dirs) override; void AddAbsoluteSearchDirs(std::vector< std::string > dirs) override; protected: private: QList<PythonCommandObserver*> m_Observer; ctkAbstractPythonManager m_PythonManager; bool m_ItkWrappingAvailable; bool m_OpenCVWrappingAvailable; bool m_VtkWrappingAvailable; bool m_ErrorOccured; }; } #endif diff --git a/Modules/Python/mitkIPythonService.h b/Modules/Python/mitkIPythonService.h index eb9c4d1dad..e53457d24a 100644 --- a/Modules/Python/mitkIPythonService.h +++ b/Modules/Python/mitkIPythonService.h @@ -1,153 +1,155 @@ /*============================================================================ 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 mitkIPythonService_h #define mitkIPythonService_h // mitk +#include <mitkImage.h> +#include <mitkSurface.h> #include <MitkPythonExports.h> -#include "mitkImage.h" + //for microservices #include <mitkServiceInterface.h> -#include "mitkSurface.h" + #include <vector> class ctkAbstractPythonManager; namespace mitk { /// /// describes a python variable (data container) /// \see IPythonService::GetVariableStack() /// struct PythonVariable { std::string m_Name; std::string m_Type; std::string m_Value; }; /// /// a PythonCommandObserver gets informed as soon as a python command was issued /// \see IPythonService::AddPythonCommandObserver() /// class PythonCommandObserver { public: virtual void CommandExecuted(const std::string& pythonCommand) = 0; }; /// /// The central service for issuing Python Code /// The class also enables to transfer mitk images to python as itk::Image and vice versa /// \see IPythonService::GetVariableStack() /// class MITKPYTHON_EXPORT IPythonService { public: /// /// Constant representing a single line command /// \see IPythonService::Execute() static const int SINGLE_LINE_COMMAND = 0; /// /// Constant representing a command in which the commands are seperated by new lines, i.e. "\\n" /// \see IPythonService::Execute() static const int MULTI_LINE_COMMAND = 1; /// /// Constant representing a single line command x which is run as "eval(x)" /// \see IPythonService::Execute() static const int EVAL_COMMAND = 2; /// /// Executes a python command. /// \return A variant containing the return value as string of the python code (if any) virtual std::string Execute( const std::string& pythonCommand, int commandType = SINGLE_LINE_COMMAND ) = 0; /// /// Executes a python script. virtual void ExecuteScript( const std::string& pathToPythonScript ) = 0; /// /// \return true if the last call to Execute...() resulted in an error, false otherwise virtual bool PythonErrorOccured() const = 0; /// /// \return The list of variables in the __main__ namespace virtual std::vector<PythonVariable> GetVariableStack() const = 0; /// /// \return true if a variable with this name is defined in the __main__ namespace, false otherwise virtual bool DoesVariableExist(const std::string& name) const = 0; /// /// \return value of variable with this name as string, empty string if variable does not exist virtual std::string GetVariable(const std::string& name) const = 0; /// /// adds a command observer which is informed after a command was issued with "Execute" virtual void AddPythonCommandObserver( PythonCommandObserver* observer ) = 0; /// /// removes a specific command observer virtual void RemovePythonCommandObserver( PythonCommandObserver* observer ) = 0; /// /// notify all observer. this should only be used if it can be garantueed that the /// current python interpreter instance got another command from anywhere else /// the the Execute() method of this service, e.g. the shell widget uses this function /// since it does not use Execute() virtual void NotifyObserver( const std::string& command ) = 0; /// /// \return true, if itk wrapping is available, false otherwise virtual bool IsSimpleItkPythonWrappingAvailable() = 0; /// /// copies an mitk image as itk image into the python interpreter process /// the image will be available as "varName" in python if everythin worked /// \return true if image was copied, else false virtual bool CopyToPythonAsSimpleItkImage( mitk::Image* image, const std::string& varName ) = 0; /// /// copies an itk image from the python process that is named "varName" /// \return the image or 0 if copying was not possible virtual mitk::Image::Pointer CopySimpleItkImageFromPython( const std::string& varName ) = 0; /// /// \return true, if OpenCv wrapping is available, false otherwise virtual bool IsOpenCvPythonWrappingAvailable() = 0; /// /// \see CopyToPythonAsItkImage() virtual bool CopyToPythonAsCvImage( mitk::Image* image, const std::string& varName ) = 0; /// /// \see CopyCvImageFromPython() virtual mitk::Image::Pointer CopyCvImageFromPython( const std::string& varName ) = 0; /// /// \return true, if vtk wrapping is available, false otherwise virtual bool IsVtkPythonWrappingAvailable() = 0; /// /// \see CopyToPythonAsItkImage() virtual bool CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& varName ) = 0; /// /// \see CopyCvImageFromPython() virtual mitk::Surface::Pointer CopyVtkPolyDataFromPython( const std::string& varName ) = 0; /// \return the ctk abstract python manager instance virtual ctkAbstractPythonManager* GetPythonManager() = 0; /// /// nothing to do here virtual ~IPythonService(); // leer in mitkIPythonService.cpp implementieren // force us module loading by linking static std::string ForceLoadModule(); virtual void AddRelativeSearchDirs(std::vector< std::string > dirs) = 0; virtual void AddAbsoluteSearchDirs(std::vector< std::string > dirs) = 0; }; } MITK_DECLARE_SERVICE_INTERFACE(mitk::IPythonService, "org.mitk.services.IPythonService") #endif diff --git a/Modules/QtPython/QmitkCtkPythonShell.cpp b/Modules/QtPython/QmitkCtkPythonShell.cpp index ebdbbf36d8..dec9e9168a 100644 --- a/Modules/QtPython/QmitkCtkPythonShell.cpp +++ b/Modules/QtPython/QmitkCtkPythonShell.cpp @@ -1,94 +1,98 @@ /*============================================================================ 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 "QmitkCtkPythonShell.h" #include <ctkAbstractPythonManager.h> +#ifdef snprintf +#undef snprintf +#endif + +#include <mitkIPythonService.h> #include <QDragEnterEvent> #include <QDropEvent> #include <QMimeData> #include <QUrl> -#include "mitkIPythonService.h" #include <usModuleContext.h> #include <usServiceReference.h> #include <usGetModuleContext.h> struct QmitkCtkPythonShellData { mitk::IPythonService* m_PythonService; us::ServiceReference<mitk::IPythonService> m_PythonServiceRef; }; QmitkCtkPythonShell::QmitkCtkPythonShell(QWidget* parent) : ctkPythonConsole(parent), d( new QmitkCtkPythonShellData ) { this->setWelcomeTextColor(Qt::green); this->setPromptColor(Qt::gray); this->setStdinTextColor(Qt::white); this->setCommandTextColor(Qt::white); this->setOutputTextColor(Qt::white); MITK_DEBUG("QmitkCtkPythonShell") << "retrieving IPythonService"; us::ModuleContext* context = us::GetModuleContext(); d->m_PythonServiceRef = context->GetServiceReference<mitk::IPythonService>(); d->m_PythonService = dynamic_cast<mitk::IPythonService*> ( context->GetService<mitk::IPythonService>(d->m_PythonServiceRef) ); MITK_DEBUG("QmitkCtkPythonShell") << "checking IPythonService"; Q_ASSERT( d->m_PythonService ); MITK_DEBUG("QmitkCtkPythonShell") << "initialize m_PythonService"; this->initialize( d->m_PythonService->GetPythonManager() ); MITK_DEBUG("QmitkCtkPythonShell") << "m_PythonService initialized"; mitk::IPythonService::ForceLoadModule(); } QmitkCtkPythonShell::~QmitkCtkPythonShell() { us::ModuleContext* context = us::GetModuleContext(); context->UngetService( d->m_PythonServiceRef ); delete d; } void QmitkCtkPythonShell::dragEnterEvent(QDragEnterEvent *event) { event->accept(); } void QmitkCtkPythonShell::dropEvent(QDropEvent *event) { QList<QUrl> urls = event->mimeData()->urls(); for(int i = 0; i < urls.size(); i++) { d->m_PythonService->Execute( urls[i].toString().toStdString(), mitk::IPythonService::SINGLE_LINE_COMMAND ); } } bool QmitkCtkPythonShell::canInsertFromMimeData(const QMimeData *) const { return true; } void QmitkCtkPythonShell::executeCommand(const QString& command) { MITK_DEBUG("QmitkCtkPythonShell") << "executing command " << command.toStdString(); d->m_PythonService->Execute(command.toStdString(),mitk::IPythonService::MULTI_LINE_COMMAND); d->m_PythonService->NotifyObserver(command.toStdString()); } void QmitkCtkPythonShell::Paste(const QString &command) { if( this->isVisible() ) { this->exec( command ); //this->executeCommand( command ); } } diff --git a/Modules/QtWidgets/resource/GeometryDataIcon.svg b/Modules/QtWidgets/resource/GeometryDataIcon.svg new file mode 100644 index 0000000000..090de6c5ef --- /dev/null +++ b/Modules/QtWidgets/resource/GeometryDataIcon.svg @@ -0,0 +1,19 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="32" height="32" version="1.1" viewBox="0 0 8.4667 8.4667" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> +<metadata> +<rdf:RDF> +<cc:Work rdf:about=""> +<dc:format>image/svg+xml</dc:format> +<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> +</cc:Work> +</rdf:RDF> +</metadata> +<g fill="none" stroke="#00ff00" stroke-width=".26458"> +<rect x=".39687" y="2.0607" width="6.009" height="6.009" rx="0" ry="0"/> +<rect x="2.6642" y=".41117" width="5.3557" height="5.3557" rx="0" ry="0"/> +<path d="m2.6175 0.38741-2.2732 1.6487" fill="#000"/> +<path d="m8.0199 0.41117-1.614 1.6496" fill="#000"/> +<path d="m8.0452 5.8174-1.614 2.3101" fill="#000"/> +<path d="m2.6642 5.7669-2.2673 2.3029" fill="#000"/> +</g> +</svg> diff --git a/Modules/QtWidgets/resource/Qmitk.qrc b/Modules/QtWidgets/resource/Qmitk.qrc index 377d75c396..f284857d3e 100644 --- a/Modules/QtWidgets/resource/Qmitk.qrc +++ b/Modules/QtWidgets/resource/Qmitk.qrc @@ -1,34 +1,36 @@ <RCC> <qresource prefix="/Qmitk"> <file>Binaerbilder_48.png</file> <file>Images_48.png</file> <file>PointSet_48.png</file> <file>Segmentation_48.png</file> <file>Surface_48.png</file> <file>mm_pointer.png</file> <file>mm_scroll.png</file> <file>mm_zoom.png</file> <file>mm_contrast.png</file> <file>mm_pan.png</file> <file>LabelSetImage_48.png</file> <file>mwLayout.png</file> <file>mwSynchronized.png</file> <file>mwDesynchronized.png</file> <file>mwMITK.png</file> <file>mwPACS.png</file> <file>star-solid.svg</file> <file>history-solid.svg</file> <file>tree_inspector.svg</file> <file>list-solid.svg</file> <file>favorite_add.svg</file> <file>favorite_remove.svg</file> <file>hourglass-half-solid.svg</file> <file>times.svg</file> <file>reset.svg</file> <file>lock.svg</file> <file>unlock.svg</file> <file>invisible.svg</file> <file>visible.svg</file> <file>SegmentationTaskListIcon.svg</file> + <file>ROIIcon.svg</file> + <file>GeometryDataIcon.svg</file> </qresource> </RCC> diff --git a/Modules/QtWidgets/resource/ROIIcon.svg b/Modules/QtWidgets/resource/ROIIcon.svg new file mode 100644 index 0000000000..dc1421047b --- /dev/null +++ b/Modules/QtWidgets/resource/ROIIcon.svg @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<svg width="32" height="32" version="1.1" viewBox="0 0 8.4667 8.4667" xmlns="http://www.w3.org/2000/svg" xmlns:cc="http://creativecommons.org/ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> +<metadata> +<rdf:RDF> +<cc:Work rdf:about=""> +<dc:format>image/svg+xml</dc:format> +<dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage"/> +</cc:Work> +</rdf:RDF> +</metadata> +<g fill="none" stroke-linecap="round" stroke-linejoin="round"> +<rect x=".39687" y=".39687" width="7.6729" height="7.6729" rx=".52917" ry=".52917" stroke="#00ff00" stroke-width=".26458"/> +<rect x="2.249" y="2.7781" width="3.9688" height="2.9104" rx=".52917" ry=".52917" stroke="#ff00ff" stroke-width=".39688"/> +</g> +</svg> diff --git a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp index bd279a298a..ab142499b0 100644 --- a/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp +++ b/Modules/QtWidgets/src/QmitkNodeDescriptorManager.cpp @@ -1,175 +1,183 @@ /*============================================================================ 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 "QmitkNodeDescriptorManager.h" #include <memory> #include <mitkNodePredicateAnd.h> #include <mitkNodePredicateDataType.h> #include <mitkNodePredicateNot.h> #include <mitkNodePredicateProperty.h> #include <mitkProperties.h> #include <QmitkStyleManager.h> #include <QList> #include <QSet> QmitkNodeDescriptorManager* QmitkNodeDescriptorManager::GetInstance() { static QmitkNodeDescriptorManager instance; return &instance; } void QmitkNodeDescriptorManager::Initialize() { auto isImage = mitk::NodePredicateDataType::New("Image"); AddDescriptor(new QmitkNodeDescriptor(tr("Image"), QString(":/Qmitk/Images_48.png"), isImage, this)); auto isMultiComponentImage = mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateProperty::New("Image.Displayed Component")); AddDescriptor(new QmitkNodeDescriptor(tr("MultiComponentImage"), QString(": / Qmitk / Images_48.png"), isMultiComponentImage, this)); auto isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); auto isBinaryImage = mitk::NodePredicateAnd::New(isBinary, isImage); AddDescriptor(new QmitkNodeDescriptor(tr("ImageMask"), QString(":/Qmitk/Binaerbilder_48.png"), isBinaryImage, this)); auto isLabelSetImage = mitk::NodePredicateDataType::New("LabelSetImage"); AddDescriptor(new QmitkNodeDescriptor(tr("LabelSetImage"), QString(":/Qmitk/LabelSetImage_48.png"), isLabelSetImage, this)); auto segmentationTaskListIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/Qmitk/SegmentationTaskListIcon.svg")); 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 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)); auto isSurface = mitk::NodePredicateDataType::New("Surface"); AddDescriptor(new QmitkNodeDescriptor(tr("Surface"), QString(":/Qmitk/Surface_48.png"), isSurface, this)); auto isNotBinary = mitk::NodePredicateNot::New(isBinary); auto isNoneBinaryImage = mitk::NodePredicateAnd::New(isImage, isNotBinary); AddDescriptor(new QmitkNodeDescriptor(tr("NoneBinaryImage"), QString(":/Qmitk/Images_48.png"), isNoneBinaryImage, this)); } void QmitkNodeDescriptorManager::AddDescriptor(QmitkNodeDescriptor* descriptor) { descriptor->setParent(this); m_NodeDescriptors.push_back(descriptor); } void QmitkNodeDescriptorManager::RemoveDescriptor(QmitkNodeDescriptor* descriptor) { int index = m_NodeDescriptors.indexOf(descriptor); if (index != -1) { m_NodeDescriptors.removeAt(index); descriptor->setParent(nullptr); delete descriptor; } } QmitkNodeDescriptor* QmitkNodeDescriptorManager::GetDescriptor(const mitk::DataNode* node) const { QmitkNodeDescriptor* descriptor = m_UnknownDataNodeDescriptor; for (QList<QmitkNodeDescriptor *>::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if ((*it)->CheckNode(node)) descriptor = *it; } return descriptor; } QmitkNodeDescriptor* QmitkNodeDescriptorManager::GetDescriptor(const QString& className) const { QmitkNodeDescriptor* descriptor = nullptr; if (className == "Unknown") { return m_UnknownDataNodeDescriptor; } else { for (QList<QmitkNodeDescriptor *>::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if ((*it)->GetNameOfClass() == className) descriptor = *it; } } return descriptor; } QList<QAction*> QmitkNodeDescriptorManager::GetActions(const mitk::DataNode* node) const { QList<QAction*> actions = m_UnknownDataNodeDescriptor->GetBatchActions(); actions.append(m_UnknownDataNodeDescriptor->GetActions()); QmitkNodeDescriptor* lastDescriptor = m_UnknownDataNodeDescriptor; for (QList<QmitkNodeDescriptor *>::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if ((*it)->CheckNode(node)) { actions.append(lastDescriptor->GetSeparator()); lastDescriptor = *it; actions.append(lastDescriptor->GetBatchActions()); actions.append(lastDescriptor->GetActions()); } } return actions; } QList<QAction*> QmitkNodeDescriptorManager::GetActions(const QList<mitk::DataNode::Pointer>& nodes) const { QList<QAction*> actions = m_UnknownDataNodeDescriptor->GetBatchActions(); QmitkNodeDescriptor* lastDescriptor = m_UnknownDataNodeDescriptor; // find all descriptors for the nodes (unique) QSet<QmitkNodeDescriptor*> nodeDescriptors; for (const auto& node : nodes) { for (QList<QmitkNodeDescriptor*>::const_iterator it = m_NodeDescriptors.begin(); it != m_NodeDescriptors.end(); ++it) { if ((*it)->CheckNode(node)) { nodeDescriptors.insert(*it); } } } // add all actions for the found descriptors for (const auto& nodeDescriptor : nodeDescriptors) { actions.append(lastDescriptor->GetSeparator()); lastDescriptor = nodeDescriptor; actions.append(lastDescriptor->GetBatchActions()); } return actions; } QmitkNodeDescriptorManager::QmitkNodeDescriptorManager() : m_UnknownDataNodeDescriptor(new QmitkNodeDescriptor("Unknown", QString(":/Qmitk/DataTypeUnknown_48.png"), nullptr, this)) { Initialize(); } QmitkNodeDescriptorManager::~QmitkNodeDescriptorManager() { // delete m_UnknownDataNodeDescriptor; // qDeleteAll(m_NodeDescriptors); } QmitkNodeDescriptor *QmitkNodeDescriptorManager::GetUnknownDataNodeDescriptor() const { return m_UnknownDataNodeDescriptor; } diff --git a/Modules/ROI/CMakeLists.txt b/Modules/ROI/CMakeLists.txt new file mode 100644 index 0000000000..e4ab9d0abe --- /dev/null +++ b/Modules/ROI/CMakeLists.txt @@ -0,0 +1,6 @@ +mitk_create_module( + DEPENDS MitkCore + PACKAGE_DEPENDS VTK|RenderingAnnotation +) + +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 0000000000..984bfe1eaa --- /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 0000000000..cd11c12ddf --- /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 0000000000..75bdad237e --- /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 <mitkCustomMimeType.h> +#include <MitkROIIOExports.h> + +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<CustomMimeType*> 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 0000000000..ade9c8016d --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.cpp @@ -0,0 +1,213 @@ +/*============================================================================ + +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 <mitkProportionalTimeGeometry.h> +#include <mitkROI.h> +#include <mitkROIIOMimeTypes.h> + +#include <filesystem> +#include <fstream> + +namespace +{ + int CheckFileFormat(const nlohmann::json& json) + { + if ("MITK ROI" != json["FileFormat"].get<std::string>()) + mitkThrow() << "Unknown file format (expected \"MITK ROI\")!"; + + auto version = json["Version"].get<int>(); + + 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 (!jGeometry.is_object()) + mitkThrow() << "Geometry is expected to be a JSON object."; + + if (jGeometry.contains("Origin")) + geometry->SetOrigin(jGeometry["Origin"].get<mitk::Point3D>()); + + if (jGeometry.contains("Spacing")) + geometry->SetSpacing(jGeometry["Spacing"].get<mitk::Vector3D>()); + + if (jGeometry.contains("Size")) + SetSize(geometry, jGeometry["Size"].get<mitk::Vector3D>()); + + auto timeSteps = jGeometry.contains("TimeSteps") + ? jGeometry["TimeSteps"].get<mitk::TimeStepType>() + : 1; + + auto result = mitk::ProportionalTimeGeometry::New(); + result->Initialize(geometry, timeSteps); + + 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() + : AbstractFileIO(ROI::GetStaticNameOfClass(), MitkROIIOMimeTypes::ROI_MIMETYPE(), "MITK ROI") +{ + this->RegisterService(); +} + +std::vector<mitk::BaseData::Pointer> mitk::ROIIO::DoRead() +{ + 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; + } + + auto result = ROI::New(); + + try + { + auto j = nlohmann::json::parse(*stream); + + CheckFileFormat(j); + + auto geometry = ReadGeometry(j["Geometry"]); + result->SetTimeGeometry(geometry); + + if (j.contains("Name")) + result->SetProperty("name", mitk::StringProperty::New(j["Name"].get<std::string>())); + + if (j.contains("Caption")) + result->SetProperty("caption", mitk::StringProperty::New(j["Caption"].get<std::string>())); + + for (const auto& roi : j["ROIs"]) + result->AddElement(roi.get<ROI::Element>()); + } + catch (const nlohmann::json::exception &e) + { + mitkThrow() << e.what(); + } + + return { result }; +} + +void mitk::ROIIO::Write() +{ + auto input = dynamic_cast<const ROI*>(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 } + }; + + 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()) + 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 +{ + return new ROIIO(*this); +} diff --git a/Modules/Core/src/DataManagement/mitkVector.cpp b/Modules/ROI/autoload/IO/src/mitkROIIO.h similarity index 52% rename from Modules/Core/src/DataManagement/mitkVector.cpp rename to Modules/ROI/autoload/IO/src/mitkROIIO.h index a9ca6ccfb5..dee6434802 100644 --- a/Modules/Core/src/DataManagement/mitkVector.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROIIO.h @@ -1,13 +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 "mitkVector.h" +#ifndef mitkROIIO_h +#define mitkROIIO_h + +#include <mitkAbstractFileIO.h> + +namespace mitk +{ + class ROIIO : public AbstractFileIO + { + public: + ROIIO(); + + using AbstractFileReader::Read; + void Write() override; + + protected: + std::vector<BaseData::Pointer> 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 0000000000..91b8032fe5 --- /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 <mitkROIIOMimeTypes.h> +#include <mitkIOMimeTypes.h> + +#include <filesystem> +#include <fstream> + +#include <nlohmann/json.hpp> + +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<int>("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::CustomMimeType*> mitk::MitkROIIOMimeTypes::Get() +{ + std::vector<CustomMimeType*> 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 0000000000..2c9304f004 --- /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 <mitkROIIOMimeTypes.h> +#include "mitkROIIO.h" + +#include <usModuleContext.h> + +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<ROIIO>()); +} + +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 0000000000..d7c3378a01 --- /dev/null +++ b/Modules/ROI/autoload/IO/src/mitkROIIOModuleActivator.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. + +============================================================================*/ + +#include <usModuleActivator.h> + +#include <memory> +#include <vector> + +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<std::shared_ptr<AbstractFileIO>> 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 0000000000..6148f54324 --- /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 <mitkROI.h> +#include <mitkIOUtil.h> + +MITK_REGISTER_SERIALIZER(ROISerializer) + +mitk::ROISerializer::ROISerializer() +{ +} + +mitk::ROISerializer::~ROISerializer() +{ +} + +std::string mitk::ROISerializer::Serialize() +{ + auto roi = dynamic_cast<const ROI*>(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/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp b/Modules/ROI/autoload/IO/src/mitkROISerializer.h similarity index 51% copy from Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp copy to Modules/ROI/autoload/IO/src/mitkROISerializer.h index 0902061f1c..0daf876a01 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp +++ b/Modules/ROI/autoload/IO/src/mitkROISerializer.h @@ -1,26 +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. ============================================================================*/ -#include <mitkBoundingShapeObjectFactory.h> +#ifndef mitkROISerializer_h +#define mitkROISerializer_h -#include "mitkPluginActivator.h" -#include "QmitkImageCropperView.h" +#include <mitkBaseDataSerializer.h> -void mitk::mitkPluginActivator::start(ctkPluginContext* context) +namespace mitk { - RegisterBoundingShapeObjectFactory(); - BERRY_REGISTER_EXTENSION_CLASS(QmitkImageCropperView, context) + class ROISerializer : public BaseDataSerializer + { + public: + mitkClassMacro(ROISerializer, BaseDataSerializer) + itkFactorylessNewMacro(Self) + + std::string Serialize() override; + + protected: + ROISerializer(); + ~ROISerializer() override; + }; } -void mitk::mitkPluginActivator::stop(ctkPluginContext*) -{ -} +#endif diff --git a/Modules/ROI/files.cmake b/Modules/ROI/files.cmake new file mode 100644 index 0000000000..1de29e9b16 --- /dev/null +++ b/Modules/ROI/files.cmake @@ -0,0 +1,17 @@ +set(H_FILES + include/mitkROI.h + include/mitkROIMapper2D.h + include/mitkROIMapper3D.h + include/mitkROIMapperLocalStorage.h + src/mitkROIMapperHelper.h + src/mitkROIObjectFactory.h +) + +set(CPP_FILES + mitkROI.cpp + mitkROIMapper2D.cpp + mitkROIMapper3D.cpp + mitkROIMapperHelper.cpp + mitkROIMapperLocalStorage.cpp + mitkROIObjectFactory.cpp +) diff --git a/Modules/ROI/include/mitkROI.h b/Modules/ROI/include/mitkROI.h new file mode 100644 index 0000000000..25fb2ad7bc --- /dev/null +++ b/Modules/ROI/include/mitkROI.h @@ -0,0 +1,202 @@ +/*============================================================================ + +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 <mitkBaseData.h> +#include <MitkROIExports.h> + +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 + * 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 <tt>"{name} ({ID})"</tt>. Braces + * are used to define placeholders which are replaced by their corresponding ROI::Element properties. + * <tt>{ID}</tt> 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 MITKROIPage 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: + using PointsType = std::map<TimeStepType, Point3D>; + using PropertyListsType = std::map<TimeStepType, PropertyList::Pointer>; + + Element(); + 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; + BaseProperty::ConstPointer GetConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext = true) const; + + /** \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<std::string> GetPropertyKeys(const std::string& contextName = "", bool includeDefaultContext = false) const override; + std::vector<std::string> GetPropertyKeys(TimeStepType t, bool includeDefaultContext = false) const; + + /** \brief Get all property context names (stringified time steps). + */ + std::vector<std::string> 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; + BaseProperty* GetNonConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext = true); + + /** \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; + void SetProperty(const std::string& propertyKey, BaseProperty* property, TimeStepType t, bool fallBackOnDefaultContext = false); + + /** \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; + void RemoveProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext = false); + + 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<TimeStepType> GetTimeSteps() 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); + + /** \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: + 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::map<unsigned int, Element>; + using Iterator = ElementsType::iterator; + 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; + Element& GetElement(unsigned int id); + + ConstIterator begin() const; + ConstIterator end() const; + + Iterator begin(); + Iterator end(); + + 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; + + 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/include/mitkROIMapper2D.h b/Modules/ROI/include/mitkROIMapper2D.h new file mode 100644 index 0000000000..173915aaec --- /dev/null +++ b/Modules/ROI/include/mitkROIMapper2D.h @@ -0,0 +1,58 @@ +/*============================================================================ + +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 <mitkLocalStorageHandler.h> +#include <mitkROIMapperLocalStorage.h> +#include <mitkVtkMapper.h> +#include <MitkROIExports.h> + +namespace mitk +{ + class MITKROI_EXPORT ROIMapper2D : public VtkMapper + { + class LocalStorage : public ROIMapperLocalStorage + { + public: + LocalStorage(); + ~LocalStorage() override; + + const PlaneGeometry* GetLastPlaneGeometry() const; + void SetLastPlaneGeometry(const PlaneGeometry* planeGeometry); + + protected: + PlaneGeometry::ConstPointer m_LastPlaneGeometry; + }; + + 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; + + void GenerateDataForRenderer(BaseRenderer* renderer) override; + void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override; + + private: + LocalStorageHandler<LocalStorage> m_LocalStorageHandler; + }; +} + +#endif diff --git a/Modules/ROI/include/mitkROIMapper3D.h b/Modules/ROI/include/mitkROIMapper3D.h new file mode 100644 index 0000000000..e57d3b148a --- /dev/null +++ b/Modules/ROI/include/mitkROIMapper3D.h @@ -0,0 +1,52 @@ +/*============================================================================ + +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 <mitkLocalStorageHandler.h> +#include <mitkROIMapperLocalStorage.h> +#include <mitkVtkMapper.h> +#include <MitkROIExports.h> + +namespace mitk +{ + class MITKROI_EXPORT ROIMapper3D : public VtkMapper + { + class LocalStorage : public ROIMapperLocalStorage + { + public: + LocalStorage(); + ~LocalStorage() override; + }; + + 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; + + void GenerateDataForRenderer(BaseRenderer* renderer) override; + void ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) override; + + private: + LocalStorageHandler<LocalStorage> m_LocalStorageHandler; + }; +} + +#endif diff --git a/Modules/ROI/include/mitkROIMapperLocalStorage.h b/Modules/ROI/include/mitkROIMapperLocalStorage.h new file mode 100644 index 0000000000..567d1eca8e --- /dev/null +++ b/Modules/ROI/include/mitkROIMapperLocalStorage.h @@ -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. + +============================================================================*/ + +#ifndef mitkROIMapperLocalStorage_h +#define mitkROIMapperLocalStorage_h + +#include <mitkMapper.h> + +template <class T> +class vtkSmartPointer; + +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: + ROIMapperLocalStorage(); + ~ROIMapperLocalStorage() override; + + vtkPropAssembly* GetPropAssembly() const; + void SetPropAssembly(vtkPropAssembly* propAssembly); + + TimePointType GetLastTimePoint() const; + void SetLastTimePoint(TimePointType timePoint); + + protected: + vtkSmartPointer<vtkPropAssembly> m_PropAssembly; + TimePointType m_LastTimePoint; + }; +} + +#endif diff --git a/Modules/ROI/src/mitkROI.cpp b/Modules/ROI/src/mitkROI.cpp new file mode 100644 index 0000000000..6f5e614b1a --- /dev/null +++ b/Modules/ROI/src/mitkROI.cpp @@ -0,0 +1,399 @@ +/*============================================================================ + +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 <mitkROI.h> + +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<unsigned int>(); + roi.SetID(id); + + if (j.contains("TimeSteps")) + { + for (const auto& jTimeStep : j["TimeSteps"]) + { + auto t = jTimeStep["t"].get<TimeStepType>(); + + roi.SetMin(jTimeStep["Min"].get<Point3D>(), t); + roi.SetMax(jTimeStep["Max"].get<Point3D>(), t); + + if (jTimeStep.contains("Properties")) + { + auto properties = mitk::PropertyList::New(); + properties->FromJSON(jTimeStep["Properties"]); + roi.SetProperties(properties, t); + } + } + } + else + { + roi.SetMin(j["Min"].get<Point3D>()); + roi.SetMax(j["Max"].get<Point3D>()); + } + + if (j.contains("Properties")) + { + auto properties = mitk::PropertyList::New(); + properties->FromJSON(j["Properties"]); + roi.SetDefaultProperties(properties); + } +} + +mitk::ROI::Element::Element() + : 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; +} + +bool mitk::ROI::Element::HasTimeSteps() const +{ + // 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::TimeStepType> mitk::ROI::Element::GetTimeSteps() const +{ + std::vector<TimeStepType> 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); +} + +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 !contextName.empty() + ? this->GetConstProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext) + : m_DefaultProperties->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 (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<std::string> mitk::ROI::Element::GetPropertyKeys(const std::string& contextName, bool includeDefaultContext) const +{ + return !contextName.empty() + ? this->GetPropertyKeys(std::stoul(contextName), includeDefaultContext) + : m_DefaultProperties->GetPropertyKeys(); +} + +std::vector<std::string> mitk::ROI::Element::GetPropertyKeys(TimeStepType t, bool includeDefaultContext) const +{ + auto it = m_Properties.find(t); + + std::vector<std::string> 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; +} + +std::vector<std::string> mitk::ROI::Element::GetPropertyContextNames() const +{ + std::vector<std::string> 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 !contextName.empty() + ? this->GetNonConstProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext) + : m_DefaultProperties->GetNonConstProperty(propertyKey); +} + +mitk::BaseProperty* mitk::ROI::Element::GetNonConstProperty(const std::string& propertyKey, TimeStepType t, bool fallBackOnDefaultContext) +{ + 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) +{ + if (!contextName.empty()) + { + this->SetProperty(propertyKey, property, std::stoul(contextName), fallBackOnDefaultContext); + return; + } + + m_DefaultProperties->SetProperty(propertyKey, property); +} + +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); +} + +void mitk::ROI::Element::RemoveProperty(const std::string& propertyKey, const std::string& contextName, bool fallBackOnDefaultContext) +{ + if (!contextName.empty()) + { + this->RemoveProperty(propertyKey, std::stoul(contextName), fallBackOnDefaultContext); + 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 (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); +} + +mitk::ROI::ROI() +{ +} + +mitk::ROI::ROI(const Self& other) + : BaseData(other) +{ +} + +mitk::ROI::~ROI() +{ +} + +size_t mitk::ROI::GetNumberOfElements() const +{ + return m_Elements.size(); +} + +void mitk::ROI::AddElement(const Element& element) +{ + 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(unsigned int id) const +{ + return m_Elements.at(id); +} + +mitk::ROI::Element& mitk::ROI::GetElement(unsigned int id) +{ + return m_Elements.at(id); +} + +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() +{ +} + +bool mitk::ROI::RequestedRegionIsOutsideOfTheBufferedRegion() +{ + return false; +} + +bool mitk::ROI::VerifyRequestedRegion() +{ + return true; +} + +void mitk::ROI::SetRequestedRegion(const itk::DataObject*) +{ +} diff --git a/Modules/ROI/src/mitkROIMapper2D.cpp b/Modules/ROI/src/mitkROIMapper2D.cpp new file mode 100644 index 0000000000..1435d4c6d7 --- /dev/null +++ b/Modules/ROI/src/mitkROIMapper2D.cpp @@ -0,0 +1,187 @@ +/*============================================================================ + +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 <mitkROIMapper2D.h> + +#include "mitkROIMapperHelper.h" + +#include <vtkCubeSource.h> +#include <vtkPlane.h> +#include <vtkPolyDataMapper.h> +#include <vtkPolyDataPlaneCutter.h> +#include <vtkPropAssembly.h> + +#include <boost/algorithm/string.hpp> + +#include <regex> + +namespace +{ + 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() +{ +} + +mitk::ROIMapper2D::LocalStorage::~LocalStorage() +{ +} + +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); + ROIMapperHelper::SetDefaultProperties(node, renderer, override); +} + +mitk::ROIMapper2D::ROIMapper2D() +{ +} + +mitk::ROIMapper2D::~ROIMapper2D() +{ +} + +void mitk::ROIMapper2D::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 (localStorage->GetLastPlaneGeometry() != nullptr && localStorage->GetLastPlaneGeometry()->IsOnPlane(planeGeometry) && + localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime() && + localStorage->GetLastTimePoint() == timePoint) + { + return; + } + + localStorage->SetLastPlaneGeometry(planeGeometry->Clone()); + localStorage->SetLastTimePoint(timePoint); + + auto data = static_cast<const ROI*>(this->GetData()); + + if (!data->GetTimeGeometry()->IsValidTimePoint(timePoint)) + return; + + const auto t = data->GetTimeGeometry()->TimePointToTimeStep(timePoint); + auto propAssembly = vtkSmartPointer<vtkPropAssembly>::New(); + + if (dataNode->IsVisible(renderer)) + { + const auto* geometry = data->GetGeometry(t); + const auto halfSpacing = geometry->GetSpacing() * 0.5f; + + auto plane = vtkSmartPointer<vtkPlane>::New(); + plane->SetOrigin(planeGeometry->GetOrigin().data()); + plane->SetNormal(planeGeometry->GetNormal().data()); + + for (const auto& [id, roi] : *data) + { + if (!roi.HasTimeStep(t)) + continue; + + Point3D min; + geometry->IndexToWorld(roi.GetMin(t), min); + min -= halfSpacing; + + Point3D max; + geometry->IndexToWorld(roi.GetMax(t), max); + max += halfSpacing; + + auto cube = vtkSmartPointer<vtkCubeSource>::New(); + cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); + + auto cutter = vtkSmartPointer<vtkPolyDataPlaneCutter>::New(); + cutter->SetInputConnection(cube->GetOutputPort()); + cutter->SetPlane(plane); + cutter->Update(); + + auto* slicePolyData = cutter->GetOutput(); + + if (slicePolyData->GetNumberOfLines() == 0) + continue; + + auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); + mapper->SetInputConnection(cutter->GetOutputPort()); + + auto actor = vtkSmartPointer<vtkActor>::New(); + actor->SetMapper(mapper); + + this->ApplyColorAndOpacityProperties(renderer, actor); + ROIMapperHelper::ApplyIndividualProperties(roi, t, actor); + + propAssembly->AddPart(actor); + + if (std::string caption; dataNode->GetStringProperty("caption", caption, renderer)) + { + caption = ROIMapperHelper::ParseCaption(caption, roi, t); + + if (!caption.empty()) + { + auto bottomLeftPoint = GetBottomLeftPoint(slicePolyData->GetPoints(), renderer); + auto captionActor = ROIMapperHelper::CreateCaptionActor(caption, bottomLeftPoint, actor->GetProperty(), dataNode, renderer); + + propAssembly->AddPart(captionActor); + } + } + } + } + + localStorage->SetPropAssembly(propAssembly); + localStorage->UpdateGenerateDataTime(); +} + +void mitk::ROIMapper2D::ApplyColorAndOpacityProperties(BaseRenderer* renderer, vtkActor* actor) +{ + auto* property = actor->GetProperty(); + + float opacity = 1.0f; + this->GetDataNode()->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 new file mode 100644 index 0000000000..e8120de0e3 --- /dev/null +++ b/Modules/ROI/src/mitkROIMapper3D.cpp @@ -0,0 +1,118 @@ +/*============================================================================ + +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 <mitkROIMapper3D.h> +#include <mitkROI.h> + +#include "mitkROIMapperHelper.h" + +#include <vtkActor.h> +#include <vtkCubeSource.h> +#include <vtkPolyDataMapper.h> +#include <vtkSmartPointer.h> + +mitk::ROIMapper3D::LocalStorage::LocalStorage() +{ +} + +mitk::ROIMapper3D::LocalStorage::~LocalStorage() +{ +} + +void mitk::ROIMapper3D::SetDefaultProperties(DataNode* node, BaseRenderer* renderer, bool override) +{ + Superclass::SetDefaultProperties(node, renderer, override); + ROIMapperHelper::SetDefaultProperties(node, renderer, override); +} + +mitk::ROIMapper3D::ROIMapper3D() +{ +} + +mitk::ROIMapper3D::~ROIMapper3D() +{ +} + +void mitk::ROIMapper3D::GenerateDataForRenderer(BaseRenderer* renderer) +{ + const auto timePoint = renderer->GetWorldTimeGeometry()->TimeStepToTimePoint(renderer->GetTimeStep()); + auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); + const auto* dataNode = this->GetDataNode(); + + if (localStorage->GetLastGenerateDataTime() >= dataNode->GetMTime() && + localStorage->GetLastTimePoint() == timePoint) + { + return; + } + + localStorage->SetLastTimePoint(timePoint); + + auto data = static_cast<const ROI*>(this->GetData()); + + if (!data->GetTimeGeometry()->IsValidTimePoint(timePoint)) + return; + + const auto t = data->GetTimeGeometry()->TimePointToTimeStep(timePoint); + auto propAssembly = vtkSmartPointer<vtkPropAssembly>::New(); + + if (dataNode->IsVisible(renderer)) + { + const auto* geometry = data->GetGeometry(); + const auto halfSpacing = geometry->GetSpacing() * 0.5f; + + for (const auto& [id, roi] : *data) + { + if (!roi.HasTimeStep(t)) + continue; + + Point3D min; + geometry->IndexToWorld(roi.GetMin(t), min); + min -= halfSpacing; + + Point3D max; + geometry->IndexToWorld(roi.GetMax(t), max); + max += halfSpacing; + + auto cube = vtkSmartPointer<vtkCubeSource>::New(); + cube->SetBounds(min[0], max[0], min[1], max[1], min[2], max[2]); + cube->Update(); + + auto mapper = vtkSmartPointer<vtkPolyDataMapper>::New(); + mapper->SetInputConnection(cube->GetOutputPort()); + + auto actor = vtkSmartPointer<vtkActor>::New(); + actor->SetMapper(mapper); + + this->ApplyColorAndOpacityProperties(renderer, actor); + ROIMapperHelper::ApplyIndividualProperties(roi, t, 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->GetDataNode()->GetOpacity(opacity, renderer); + property->SetOpacity(opacity); +} + +vtkProp* mitk::ROIMapper3D::GetVtkProp(BaseRenderer* renderer) +{ + return m_LocalStorageHandler.GetLocalStorage(renderer)->GetPropAssembly(); +} diff --git a/Modules/ROI/src/mitkROIMapperHelper.cpp b/Modules/ROI/src/mitkROIMapperHelper.cpp new file mode 100644 index 0000000000..68dc54c545 --- /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 <vtkTextActor.h> +#include <vtkTextProperty.h> + +#include <boost/algorithm/string.hpp> + +#include <regex> + +void mitk::ROIMapperHelper::ApplyIndividualProperties(const ROI::Element& roi, TimeStepType t, vtkActor* actor) +{ + auto* property = actor->GetProperty(); + + property->SetRepresentationToWireframe(); + property->LightingOff(); + + if (auto colorProperty = GetConstProperty<ColorProperty>("color", roi, t); colorProperty != nullptr) + { + const auto color = colorProperty->GetColor(); + property->SetColor(color[0], color[1], color[2]); + } + + if (auto opacityProperty = GetConstProperty<FloatProperty>("opacity", roi, t); opacityProperty != nullptr) + { + const auto opacity = opacityProperty->GetValue(); + property->SetOpacity(property->GetOpacity() * opacity); + } + + if (auto lineWidthProperty = GetConstProperty<FloatProperty>("lineWidth", roi, t); lineWidthProperty != nullptr) + { + const auto lineWidth = lineWidthProperty->GetValue(); + property->SetLineWidth(lineWidth); + } +} + +vtkSmartPointer<vtkCaptionActor2D> mitk::ROIMapperHelper::CreateCaptionActor(const std::string& caption, const Point3D& attachmentPoint, vtkProperty* property, const DataNode* dataNode, const BaseRenderer* renderer) +{ + auto captionActor = vtkSmartPointer<vtkCaptionActor2D>::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, TimeStepType t) +{ + 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.GetID())); + } + else + { + auto property = roi.GetConstProperty(match[1], t); + + 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 0000000000..edab2ad05d --- /dev/null +++ b/Modules/ROI/src/mitkROIMapperHelper.h @@ -0,0 +1,60 @@ +/*============================================================================ + +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 <mitkBaseRenderer.h> +#include <mitkROI.h> + +#include <vtkCaptionActor2D.h> +#include <vtkProperty.h> +#include <vtkSmartPointer.h> + +namespace mitk +{ + namespace ROIMapperHelper + { + /** \brief Apply %ROI properties at a certain time step to the given 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. + */ + vtkSmartPointer<vtkCaptionActor2D> 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 %ROI properties. + */ + template <class T> + const T* GetConstProperty(const std::string& propertyKey, const ROI::Element& roi, TimeStepType t) + { + auto property = roi.GetConstProperty(propertyKey, t); + + if (property.IsNotNull()) + return dynamic_cast<const T*>(property.GetPointer()); + + return nullptr; + } + } +} + +#endif diff --git a/Modules/ROI/src/mitkROIMapperLocalStorage.cpp b/Modules/ROI/src/mitkROIMapperLocalStorage.cpp new file mode 100644 index 0000000000..4c57abe8df --- /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 <mitkROIMapperLocalStorage.h> + +#include <vtkPropAssembly.h> +#include <vtkSmartPointer.h> + +mitk::ROIMapperLocalStorage::ROIMapperLocalStorage() + : m_PropAssembly(vtkSmartPointer<vtkPropAssembly>::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; +} diff --git a/Modules/ROI/src/mitkROIObjectFactory.cpp b/Modules/ROI/src/mitkROIObjectFactory.cpp new file mode 100644 index 0000000000..cffe1fca6d --- /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 <mitkCoreObjectFactory.h> +#include <mitkROI.h> +#include <mitkROIMapper2D.h> +#include <mitkROIMapper3D.h> + +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<ROI*>(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<ROI*>(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 0000000000..75ac43fc92 --- /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 <mitkCoreObjectFactoryBase.h> + +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 diff --git a/Modules/RT/include/mitkIsoDoseLevelSetProperty.h b/Modules/RT/include/mitkIsoDoseLevelSetProperty.h index 32aa056beb..84bce165a9 100644 --- a/Modules/RT/include/mitkIsoDoseLevelSetProperty.h +++ b/Modules/RT/include/mitkIsoDoseLevelSetProperty.h @@ -1,73 +1,76 @@ /*============================================================================ 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 mitkIsoDoseLevelSetProperty_h #define mitkIsoDoseLevelSetProperty_h #include "mitkBaseProperty.h" #include "mitkIsoDoseLevelCollections.h" #include "MitkRTExports.h" namespace mitk { /** \brief Property class for dose iso level sets. */ class MITKRT_EXPORT IsoDoseLevelSetProperty : public BaseProperty { protected: IsoDoseLevelSet::Pointer m_IsoLevelSet; IsoDoseLevelSetProperty(); explicit IsoDoseLevelSetProperty(const IsoDoseLevelSetProperty& other); explicit IsoDoseLevelSetProperty(IsoDoseLevelSet* levelSet); public: mitkClassMacro(IsoDoseLevelSetProperty, BaseProperty); itkNewMacro(IsoDoseLevelSetProperty); mitkNewMacro1Param(IsoDoseLevelSetProperty, IsoDoseLevelSet*); typedef IsoDoseLevelSet ValueType; ~IsoDoseLevelSetProperty() override; const IsoDoseLevelSet * GetIsoDoseLevelSet() const; const IsoDoseLevelSet * GetValue() const; IsoDoseLevelSet * GetIsoDoseLevelSet(); IsoDoseLevelSet * GetValue(); void SetIsoDoseLevelSet(IsoDoseLevelSet* levelSet); void SetValue(IsoDoseLevelSet* levelSet); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty& property) const override; bool Assign(const BaseProperty& property) override; }; } // namespace mitk #endif diff --git a/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h b/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h index eadc300ac4..5887234c37 100644 --- a/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h +++ b/Modules/RT/include/mitkIsoDoseLevelVectorProperty.h @@ -1,74 +1,77 @@ /*============================================================================ 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 mitkIsoDoseLevelVectorProperty_h #define mitkIsoDoseLevelVectorProperty_h #include "mitkBaseProperty.h" #include "mitkIsoDoseLevelCollections.h" #include "MitkRTExports.h" namespace mitk { /** \brief Property class for dose iso level vector. */ class MITKRT_EXPORT IsoDoseLevelVectorProperty : public BaseProperty { protected: IsoDoseLevelVector::Pointer m_IsoLevelVector; IsoDoseLevelVectorProperty(); explicit IsoDoseLevelVectorProperty(const IsoDoseLevelVectorProperty& other); explicit IsoDoseLevelVectorProperty(IsoDoseLevelVector* levelVector); public: mitkClassMacro(IsoDoseLevelVectorProperty, BaseProperty); itkNewMacro(IsoDoseLevelVectorProperty); mitkNewMacro1Param(IsoDoseLevelVectorProperty, IsoDoseLevelVector*); typedef IsoDoseLevelVector ValueType; ~IsoDoseLevelVectorProperty() override; const IsoDoseLevelVector * GetIsoDoseLevelVector() const; const IsoDoseLevelVector * GetValue() const; IsoDoseLevelVector * GetIsoDoseLevelVector(); IsoDoseLevelVector * GetValue(); void SetIsoDoseLevelVector(IsoDoseLevelVector* levelVector); void SetValue(IsoDoseLevelVector* levelVector); std::string GetValueAsString() const override; + bool ToJSON(nlohmann::json& j) const override; + bool FromJSON(const nlohmann::json& j) override; + using BaseProperty::operator=; private: itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty& property) const override; bool Assign(const BaseProperty& property) override; }; } // namespace mitk #endif diff --git a/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp b/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp index aa15834838..35b321b3ab 100644 --- a/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp +++ b/Modules/RT/src/mitkIsoDoseLevelSetProperty.cpp @@ -1,103 +1,113 @@ /*============================================================================ 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 "mitkIsoDoseLevelSetProperty.h" mitk::IsoDoseLevelSetProperty::IsoDoseLevelSetProperty() { } mitk::IsoDoseLevelSetProperty::IsoDoseLevelSetProperty(const mitk::IsoDoseLevelSetProperty& other) : BaseProperty(other) , m_IsoLevelSet(other.m_IsoLevelSet) { } mitk::IsoDoseLevelSetProperty::IsoDoseLevelSetProperty(IsoDoseLevelSet* levelSet) : m_IsoLevelSet(levelSet) { } mitk::IsoDoseLevelSetProperty::~IsoDoseLevelSetProperty() { } bool mitk::IsoDoseLevelSetProperty::IsEqual(const BaseProperty& property) const { return this->m_IsoLevelSet == static_cast<const Self&>(property).m_IsoLevelSet; } bool mitk::IsoDoseLevelSetProperty::Assign(const BaseProperty& property) { this->m_IsoLevelSet = static_cast<const Self&>(property).m_IsoLevelSet; return true; } const mitk::IsoDoseLevelSet * mitk::IsoDoseLevelSetProperty::GetIsoDoseLevelSet() const { return m_IsoLevelSet; } const mitk::IsoDoseLevelSet * mitk::IsoDoseLevelSetProperty::GetValue() const { return GetIsoDoseLevelSet(); } mitk::IsoDoseLevelSet * mitk::IsoDoseLevelSetProperty::GetIsoDoseLevelSet() { return m_IsoLevelSet; } mitk::IsoDoseLevelSet * mitk::IsoDoseLevelSetProperty::GetValue() { return GetIsoDoseLevelSet(); } void mitk::IsoDoseLevelSetProperty::SetIsoDoseLevelSet(IsoDoseLevelSet* levelSet) { if(m_IsoLevelSet != levelSet) { m_IsoLevelSet = levelSet; Modified(); } } void mitk::IsoDoseLevelSetProperty::SetValue(IsoDoseLevelSet* levelSet) { SetIsoDoseLevelSet(levelSet); } std::string mitk::IsoDoseLevelSetProperty::GetValueAsString() const { std::stringstream myStr; myStr << "IsoDoseLevels: "; if (m_IsoLevelSet.IsNotNull()) { myStr << m_IsoLevelSet->Size() << std::endl; for (IsoDoseLevelSet::ConstIterator pos = m_IsoLevelSet->Begin(); pos != m_IsoLevelSet->End(); ++pos) { myStr << " " << 100*(pos->GetDoseValue()) << "% : ("<<pos->GetColor()<< "); iso line: " << pos->GetVisibleIsoLine() << "; color wash: "<<pos->GetVisibleColorWash() << std::endl; } } 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)); return result; } diff --git a/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp b/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp index 0182f7c135..808acfa397 100644 --- a/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp +++ b/Modules/RT/src/mitkIsoDoseLevelVectorProperty.cpp @@ -1,103 +1,113 @@ /*============================================================================ 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 "mitkIsoDoseLevelVectorProperty.h" mitk::IsoDoseLevelVectorProperty::IsoDoseLevelVectorProperty() { } mitk::IsoDoseLevelVectorProperty::IsoDoseLevelVectorProperty(const mitk::IsoDoseLevelVectorProperty& other) : BaseProperty(other) , m_IsoLevelVector(other.m_IsoLevelVector) { } mitk::IsoDoseLevelVectorProperty::IsoDoseLevelVectorProperty(IsoDoseLevelVector* levelVector) : m_IsoLevelVector(levelVector) { } mitk::IsoDoseLevelVectorProperty::~IsoDoseLevelVectorProperty() { } bool mitk::IsoDoseLevelVectorProperty::IsEqual(const BaseProperty& property) const { return this->m_IsoLevelVector == static_cast<const Self&>(property).m_IsoLevelVector; } bool mitk::IsoDoseLevelVectorProperty::Assign(const BaseProperty& property) { this->m_IsoLevelVector = static_cast<const Self&>(property).m_IsoLevelVector; return true; } const mitk::IsoDoseLevelVector * mitk::IsoDoseLevelVectorProperty::GetIsoDoseLevelVector() const { return m_IsoLevelVector; } const mitk::IsoDoseLevelVector * mitk::IsoDoseLevelVectorProperty::GetValue() const { return GetIsoDoseLevelVector(); } mitk::IsoDoseLevelVector * mitk::IsoDoseLevelVectorProperty::GetIsoDoseLevelVector() { return m_IsoLevelVector; } mitk::IsoDoseLevelVector * mitk::IsoDoseLevelVectorProperty::GetValue() { return GetIsoDoseLevelVector(); } void mitk::IsoDoseLevelVectorProperty::SetIsoDoseLevelVector(IsoDoseLevelVector* levelVector) { if(m_IsoLevelVector != levelVector) { m_IsoLevelVector = levelVector; Modified(); } } void mitk::IsoDoseLevelVectorProperty::SetValue(IsoDoseLevelVector* levelVector) { SetIsoDoseLevelVector(levelVector); } std::string mitk::IsoDoseLevelVectorProperty::GetValueAsString() const { std::stringstream myStr; myStr << "IsoDoseLevels: "; if (m_IsoLevelVector.IsNotNull()) { myStr << m_IsoLevelVector->Size() << std::endl; for (IsoDoseLevelVector::ConstIterator pos = m_IsoLevelVector->Begin(); pos != m_IsoLevelVector->End(); ++pos) { myStr << " " << 100*(pos->Value()->GetDoseValue()) << "% : ("<<pos->Value()->GetColor()<< "); iso line: " << pos->Value()->GetVisibleIsoLine() << std::endl; } } 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)); return result; } diff --git a/Modules/SceneSerialization/src/mitkSceneIO.cpp b/Modules/SceneSerialization/src/mitkSceneIO.cpp index c96aa37a3e..6072a7bc3f 100644 --- a/Modules/SceneSerialization/src/mitkSceneIO.cpp +++ b/Modules/SceneSerialization/src/mitkSceneIO.cpp @@ -1,577 +1,578 @@ /*============================================================================ 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 <Poco/Delegate.h> #include <Poco/Path.h> #include <Poco/TemporaryFile.h> #include <Poco/Zip/Compress.h> #include <Poco/Zip/Decompress.h> #include "mitkBaseDataSerializer.h" #include "mitkPropertyListSerializer.h" #include "mitkSceneIO.h" #include "mitkSceneReader.h" #include "mitkBaseRenderer.h" #include "mitkProgressBar.h" #include "mitkRenderingManager.h" #include "mitkStandaloneDataStorage.h" #include <mitkLocaleSwitch.h> #include <mitkStandardFileLocations.h> +#include <mitkUIDGenerator.h> #include <itkObjectFactoryBase.h> #include <fstream> #include <mitkIOUtil.h> #include <sstream> #include "itksys/SystemTools.hxx" #include <tinyxml2.h> mitk::SceneIO::SceneIO() : m_WorkingDirectory(""), m_UnzipErrors(0) { } mitk::SceneIO::~SceneIO() { } std::string mitk::SceneIO::CreateEmptyTempDirectory() { mitk::UIDGenerator uidGen; // std::string returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + // Poco::Path::separator() + "SceneIOTemp" + uidGen.GetUID(); std::string returnValue = Poco::Path::temp() + "SceneIOTemp" + uidGen.GetUID(); std::string uniquename = returnValue + Poco::Path::separator(); Poco::File tempdir(uniquename); try { bool existsNot = tempdir.createDirectory(); if (!existsNot) { MITK_ERROR << "Warning: Directory already exitsts: " << uniquename << " (choosing another)"; returnValue = mitk::StandardFileLocations::GetInstance()->GetOptionDirectory() + Poco::Path::separator() + "SceneIOTempDirectory" + uidGen.GetUID(); uniquename = returnValue + Poco::Path::separator(); Poco::File tempdir2(uniquename); if (!tempdir2.createDirectory()) { MITK_ERROR << "Warning: Second directory also already exitsts: " << uniquename; } } } catch (std::exception &e) { MITK_ERROR << "Could not create temporary directory " << uniquename << ":" << e.what(); return ""; } return returnValue; } mitk::DataStorage::Pointer mitk::SceneIO::LoadScene(const std::string &filename, DataStorage *pStorage, bool clearStorageFirst) { mitk::LocaleSwitch localeSwitch("C"); // prepare data storage DataStorage::Pointer storage = pStorage; if (storage.IsNull()) { storage = StandaloneDataStorage::New().GetPointer(); } // test input filename if (filename.empty()) { MITK_ERROR << "No filename given. Not possible to load scene."; return storage; } // test if filename can be read std::ifstream file(filename.c_str(), std::ios::binary); if (!file.good()) { MITK_ERROR << "Cannot open '" << filename << "' for reading"; return storage; } // get new temporary directory m_WorkingDirectory = CreateEmptyTempDirectory(); if (m_WorkingDirectory.empty()) { MITK_ERROR << "Could not create temporary directory. Cannot open scene files."; return storage; } // unzip all filenames contents to temp dir m_UnzipErrors = 0; Poco::Zip::Decompress unzipper(file, Poco::Path(m_WorkingDirectory)); unzipper.EError += Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>>( this, &SceneIO::OnUnzipError); unzipper.EOk += Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path>>( this, &SceneIO::OnUnzipOk); unzipper.decompressAllFiles(); unzipper.EError -= Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string>>( this, &SceneIO::OnUnzipError); unzipper.EOk -= Poco::Delegate<SceneIO, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path>>( this, &SceneIO::OnUnzipOk); if (m_UnzipErrors) { MITK_ERROR << "There were " << m_UnzipErrors << " errors unzipping '" << filename << "'. Will attempt to read whatever could be unzipped."; } // transcode locale-dependent string m_WorkingDirectory = Poco::Path::transcode (m_WorkingDirectory); auto indexFile = m_WorkingDirectory + mitk::IOUtil::GetDirectorySeparator() + "index.xml"; storage = LoadSceneUnzipped(indexFile, storage, clearStorageFirst); // delete temp directory try { Poco::File deleteDir(m_WorkingDirectory); deleteDir.remove(true); // recursive } catch (...) { MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory; } // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method) return storage; } mitk::DataStorage::Pointer mitk::SceneIO::LoadSceneUnzipped(const std::string &indexfilename, DataStorage *pStorage, bool clearStorageFirst) { mitk::LocaleSwitch localeSwitch("C"); // prepare data storage DataStorage::Pointer storage = pStorage; if (storage.IsNull()) { storage = StandaloneDataStorage::New().GetPointer(); } if (clearStorageFirst) { try { storage->Remove(storage->GetAll()); } catch (...) { MITK_ERROR << "DataStorage cannot be cleared properly."; } } // test input filename if (indexfilename.empty()) { MITK_ERROR << "No filename given. Not possible to load scene."; return storage; } // transcode locale-dependent string std::string tempfilename; std::string workingDir; itksys::SystemTools::SplitProgramPath(indexfilename, workingDir, tempfilename); // test if index.xml exists // parse index.xml with TinyXML tinyxml2::XMLDocument document; if (tinyxml2::XML_SUCCESS != document.LoadFile(indexfilename.c_str())) { MITK_ERROR << "Could not open/read/parse " << workingDir << mitk::IOUtil::GetDirectorySeparator() << "index.xml\nTinyXML reports: " << document.ErrorStr() << std::endl; return storage; } SceneReader::Pointer reader = SceneReader::New(); if (!reader->LoadScene(document, workingDir, storage)) { MITK_ERROR << "There were errors while loading scene file " << indexfilename << ". Your data may be corrupted"; } // return new data storage, even if empty or uncomplete (return as much as possible but notify calling method) return storage; } bool mitk::SceneIO::SaveScene(DataStorage::SetOfObjects::ConstPointer sceneNodes, const DataStorage *storage, const std::string &filename) { if (!sceneNodes) { MITK_ERROR << "No set of nodes given. Not possible to save scene."; return false; } if (!storage) { MITK_ERROR << "No data storage given. Not possible to save scene."; // \TODO: Technically, it would be possible to // save the nodes without their relation return false; } if (filename.empty()) { MITK_ERROR << "No filename given. Not possible to save scene."; return false; } mitk::LocaleSwitch localeSwitch("C"); try { m_FailedNodes = DataStorage::SetOfObjects::New(); m_FailedProperties = PropertyList::New(); // start XML DOM tinyxml2::XMLDocument document; document.InsertEndChild(document.NewDeclaration()); auto *version = document.NewElement("Version"); version->SetAttribute("Writer", __FILE__); version->SetAttribute("Revision", "$Revision: 17055 $"); version->SetAttribute("FileVersion", 1); document.InsertEndChild(version); // DataStorage::SetOfObjects::ConstPointer sceneNodes = storage->GetSubset( predicate ); if (sceneNodes.IsNull()) { MITK_WARN << "Saving empty scene to " << filename; } else { if (sceneNodes->size() == 0) { MITK_WARN << "Saving empty scene to " << filename; } MITK_INFO << "Storing scene with " << sceneNodes->size() << " objects to " << filename; m_WorkingDirectory = CreateEmptyTempDirectory(); if (m_WorkingDirectory.empty()) { MITK_ERROR << "Could not create temporary directory. Cannot create scene files."; return false; } ProgressBar::GetInstance()->AddStepsToDo(sceneNodes->size()); // find out about dependencies typedef std::map<DataNode *, std::string> UIDMapType; typedef std::map<DataNode *, std::list<std::string>> SourcesMapType; UIDMapType nodeUIDs; // for dependencies: ID of each node SourcesMapType sourceUIDs; // for dependencies: IDs of a node's parent nodes UIDGenerator nodeUIDGen("OBJECT_"); for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter) { DataNode *node = iter->GetPointer(); if (!node) continue; // unlikely event that we get a nullptr pointer as an object for saving. just ignore // generate UIDs for all source objects DataStorage::SetOfObjects::ConstPointer sourceObjects = storage->GetSources(node); for (auto sourceIter = sourceObjects->begin(); sourceIter != sourceObjects->end(); ++sourceIter) { if (std::find(sceneNodes->begin(), sceneNodes->end(), *sourceIter) == sceneNodes->end()) continue; // source is not saved, so don't generate a UID for this source // create a uid for the parent object if (nodeUIDs[*sourceIter].empty()) { nodeUIDs[*sourceIter] = nodeUIDGen.GetUID(); } // store this dependency for writing sourceUIDs[node].push_back(nodeUIDs[*sourceIter]); } if (nodeUIDs[node].empty()) { nodeUIDs[node] = nodeUIDGen.GetUID(); } } // write out objects, dependencies and properties for (auto iter = sceneNodes->begin(); iter != sceneNodes->end(); ++iter) { DataNode *node = iter->GetPointer(); if (node) { auto *nodeElement = document.NewElement("node"); std::string filenameHint(node->GetName()); filenameHint = itksys::SystemTools::MakeCindentifier( filenameHint.c_str()); // escape filename <-- only allow [A-Za-z0-9_], replace everything else with _ // store dependencies auto searchUIDIter = nodeUIDs.find(node); if (searchUIDIter != nodeUIDs.end()) { // store this node's ID nodeElement->SetAttribute("UID", searchUIDIter->second.c_str()); } auto searchSourcesIter = sourceUIDs.find(node); if (searchSourcesIter != sourceUIDs.end()) { // store all source IDs for (auto sourceUIDIter = searchSourcesIter->second.begin(); sourceUIDIter != searchSourcesIter->second.end(); ++sourceUIDIter) { auto *uidElement = document.NewElement("source"); uidElement->SetAttribute("UID", sourceUIDIter->c_str()); nodeElement->InsertEndChild(uidElement); } } // store basedata if (BaseData *data = node->GetData()) { // std::string filenameHint( node->GetName() ); bool error(false); auto *dataElement = SaveBaseData(document, data, filenameHint, error); // returns a reference to a file if (error) { m_FailedNodes->push_back(node); } // store basedata properties PropertyList *propertyList = data->GetPropertyList(); if (propertyList && !propertyList->IsEmpty()) { auto *baseDataPropertiesElement = SavePropertyList(document, propertyList, filenameHint + "-data"); // returns a reference to a file dataElement->InsertEndChild(baseDataPropertiesElement); } nodeElement->InsertEndChild(dataElement); } // store all renderwindow specific propertylists mitk::DataNode::PropertyListKeyNames propertyListKeys = node->GetPropertyListNames(); for (const auto &renderWindowName : propertyListKeys) { PropertyList *propertyList = node->GetPropertyList(renderWindowName); if (propertyList && !propertyList->IsEmpty()) { auto *renderWindowPropertiesElement = SavePropertyList(document, propertyList, filenameHint + "-" + renderWindowName); // returns a reference to a file renderWindowPropertiesElement->SetAttribute("renderwindow", renderWindowName.c_str()); nodeElement->InsertEndChild(renderWindowPropertiesElement); } } // don't forget the renderwindow independent list PropertyList *propertyList = node->GetPropertyList(); if (propertyList && !propertyList->IsEmpty()) { auto *propertiesElement = SavePropertyList(document, propertyList, filenameHint + "-node"); // returns a reference to a file nodeElement->InsertEndChild(propertiesElement); } document.InsertEndChild(nodeElement); } else { MITK_WARN << "Ignoring nullptr node during scene serialization."; } ProgressBar::GetInstance()->Progress(); } // end for all nodes } // end if sceneNodes std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory ); auto xmlFilename = defaultLocale_WorkingDirectory + Poco::Path::separator() + "index.xml"; if (tinyxml2::XML_SUCCESS != document.SaveFile(xmlFilename.c_str())) { MITK_ERROR << "Could not write scene to " << defaultLocale_WorkingDirectory << Poco::Path::separator() << "index.xml" << "\nTinyXML reports '" << document.ErrorStr() << "'"; return false; } else { try { Poco::File deleteFile(filename.c_str()); if (deleteFile.exists()) { deleteFile.remove(); } // create zip at filename std::ofstream file(filename.c_str(), std::ios::binary | std::ios::out); if (!file.good()) { MITK_ERROR << "Could not open a zip file for writing: '" << filename << "'"; return false; } else { Poco::Zip::Compress zipper(file, true); Poco::Path tmpdir(m_WorkingDirectory); zipper.addRecursive(tmpdir); zipper.close(); } try { Poco::File deleteDir(m_WorkingDirectory); deleteDir.remove(true); // recursive } catch (...) { MITK_ERROR << "Could not delete temporary directory " << m_WorkingDirectory; return false; // ok? } } catch (std::exception &e) { MITK_ERROR << "Could not create ZIP file from " << m_WorkingDirectory << "\nReason: " << e.what(); return false; } return true; } } catch (std::exception &e) { MITK_ERROR << "Caught exception during saving temporary files to disk. Error description: '" << e.what() << "'"; return false; } } tinyxml2::XMLElement *mitk::SceneIO::SaveBaseData(tinyxml2::XMLDocument &doc, BaseData *data, const std::string &filenamehint, bool &error) { assert(data); error = true; // find correct serializer // the serializer must // - create a file containing all information to recreate the BaseData object --> needs to know where to put this // file (and a filename?) // - TODO what to do about writers that creates one file per timestep? auto *element = doc.NewElement("data"); element->SetAttribute("type", data->GetNameOfClass()); // construct name of serializer class std::string serializername(data->GetNameOfClass()); serializername += "Serializer"; std::list<itk::LightObject::Pointer> thingsThatCanSerializeThis = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (thingsThatCanSerializeThis.size() < 1) { MITK_ERROR << "No serializer found for " << data->GetNameOfClass() << ". Skipping object"; } for (auto iter = thingsThatCanSerializeThis.begin(); iter != thingsThatCanSerializeThis.end(); ++iter) { if (auto *serializer = dynamic_cast<BaseDataSerializer *>(iter->GetPointer())) { serializer->SetData(data); serializer->SetFilenameHint(filenamehint); std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory ); serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory); try { std::string writtenfilename = serializer->Serialize(); element->SetAttribute("file", writtenfilename.c_str()); if (!writtenfilename.empty()) error = false; } catch (std::exception &e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } break; } } element->SetAttribute("UID", data->GetUID().c_str()); return element; } tinyxml2::XMLElement *mitk::SceneIO::SavePropertyList(tinyxml2::XMLDocument &doc, PropertyList *propertyList, const std::string &filenamehint) { assert(propertyList); // - TODO what to do about shared properties (same object in two lists or behind several keys)? auto *element = doc.NewElement("properties"); // construct name of serializer class PropertyListSerializer::Pointer serializer = PropertyListSerializer::New(); serializer->SetPropertyList(propertyList); serializer->SetFilenameHint(filenamehint); std::string defaultLocale_WorkingDirectory = Poco::Path::transcode( m_WorkingDirectory ); serializer->SetWorkingDirectory(defaultLocale_WorkingDirectory); try { std::string writtenfilename = serializer->Serialize(); element->SetAttribute("file", writtenfilename.c_str()); PropertyList::Pointer failedProperties = serializer->GetFailedProperties(); if (failedProperties.IsNotNull()) { // move failed properties to global list m_FailedProperties->ConcatenatePropertyList(failedProperties, true); } } catch (std::exception &e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } return element; } const mitk::SceneIO::FailedBaseDataListType *mitk::SceneIO::GetFailedNodes() { return m_FailedNodes.GetPointer(); } const mitk::PropertyList *mitk::SceneIO::GetFailedProperties() { return m_FailedProperties; } void mitk::SceneIO::OnUnzipError(const void * /*pSender*/, std::pair<const Poco::Zip::ZipLocalFileHeader, const std::string> &info) { ++m_UnzipErrors; MITK_ERROR << "Error while unzipping: " << info.second; } void mitk::SceneIO::OnUnzipOk(const void * /*pSender*/, std::pair<const Poco::Zip::ZipLocalFileHeader, const Poco::Path> & /*info*/) { // MITK_INFO << "Unzipped ok: " << info.second.toString(); } diff --git a/Modules/SceneSerialization/src/mitkSceneReaderV1.h b/Modules/SceneSerialization/src/mitkSceneReaderV1.h index ba69c6081e..2c370b1ad8 100644 --- a/Modules/SceneSerialization/src/mitkSceneReaderV1.h +++ b/Modules/SceneSerialization/src/mitkSceneReaderV1.h @@ -1,76 +1,77 @@ /*============================================================================ 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 mitkSceneReaderV1_h #define mitkSceneReaderV1_h -#include "mitkSceneReader.h" +#include <mitkSceneReader.h> +#include <mitkUIDGenerator.h> namespace tinyxml2 { class XMLElement; } namespace mitk { class SceneReaderV1 : public SceneReader { public: mitkClassMacro(SceneReaderV1, SceneReader); itkFactorylessNewMacro(Self); itkCloneMacro(Self); bool LoadScene(tinyxml2::XMLDocument &document, const std::string &workingDirectory, DataStorage *storage) override; protected: /** \brief tries to create one DataNode from a given XML \<node\> element */ DataNode::Pointer LoadBaseDataFromDataTag(const tinyxml2::XMLElement *dataElement, const PropertyList *properties, const std::string &workingDirectory, bool &error); /** \brief reads all the properties from the XML document and recreates them in node */ bool DecorateNodeWithProperties(DataNode *node, const tinyxml2::XMLElement *nodeElement, const std::string &workingDirectory); /** \brief Clear a default property list and handle some exceptions. Called after assigning a BaseData object to a fresh DataNode via SetData(). This call to SetData() would create default properties that have not been there when saving the scene. Since they can produce problems, we clear the list and use only those properties that we read from the scene file. This method also handles some exceptions for backwards compatibility. Those exceptions are documented directly in the code of the method. */ void ClearNodePropertyListWithExceptions(DataNode &node, PropertyList &propertyList); typedef std::pair<DataNode::Pointer, std::list<std::string>> NodesAndParentsPair; typedef std::list<NodesAndParentsPair> OrderedNodesList; typedef std::map<std::string, DataNode *> IDToNodeMappingType; typedef std::map<DataNode *, std::string> NodeToIDMappingType; OrderedNodesList m_OrderedNodePairs; IDToNodeMappingType m_NodeForID; NodeToIDMappingType m_IDForNode; UIDGenerator m_UIDGen; }; } #endif diff --git a/Modules/TubeGraph/include/mitkTubeGraphProperty.h b/Modules/TubeGraph/include/mitkTubeGraphProperty.h index 87afa04b89..95fc651efe 100644 --- a/Modules/TubeGraph/include/mitkTubeGraphProperty.h +++ b/Modules/TubeGraph/include/mitkTubeGraphProperty.h @@ -1,146 +1,149 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkTubeGraphProperty_h #define mitkTubeGraphProperty_h #include <MitkTubeGraphExports.h> #include "mitkTubeGraph.h" #include <mitkBaseProperty.h> #include <mitkColorProperty.h> #include <itkObject.h> #include <map> #include <vector> namespace mitk { /** * \brief Property for tube graphs */ class MITKTUBEGRAPH_EXPORT TubeGraphProperty : public BaseProperty { public: mitkClassMacro(TubeGraphProperty, BaseProperty); itkNewMacro(TubeGraphProperty); struct LabelGroup { struct Label { std::string labelName; bool isVisible; Color labelColor; }; std::string labelGroupName; std::vector<Label *> labels; }; ///////////////TYPEDEF/////////////// typedef TubeGraph::TubeDescriptorType TubeDescriptorType; typedef std::vector<TubeGraphProperty::LabelGroup *> LabelGroupSetType; typedef std::pair<TubeDescriptorType, std::string> TubeToLabelGroupType; ///////////////////////////////////// struct Annotation { std::string name; std::string description; TubeDescriptorType tube; }; /** * Checks, if a given tube is visible * @param tube the tube id of the tube to check * @returns true, if the tube with id is visible or false otherwise */ bool IsTubeVisible(const TubeDescriptorType &tube); /** * Sets a tube active. * @param tube the tube id of the tube, which has to be set active * @param active true, if the tube should be active or false if not. */ void SetTubeActive(const TubeDescriptorType &tube, const bool &active); /** * Sets tubes active. * */ void SetTubesActive(std::vector<TubeDescriptorType> &tubes); /** * Checks, if a given tube is activated * @param tube the to check * @returns true, if the tube with id is active or false otherwise */ bool IsTubeActive(const TubeDescriptorType &tube); std::vector<TubeDescriptorType> GetActiveTubes(); Color GetColorOfTube(const TubeDescriptorType &tube); void SetTubesToLabels(std::map<TubeToLabelGroupType, std::string> tubeToLabelMap); std::map<TubeToLabelGroupType, std::string> GetTubesToLabels(); /** * Deactivates all tubes */ void DeactivateAllTubes(); void AddAnnotation(Annotation *annotation); Annotation *GetAnnotationByName(std::string annotation); std::vector<Annotation *> GetAnnotations(); void RemoveAnnotation(Annotation *annotation); void AddLabelGroup(LabelGroup *labelGroup, unsigned int position); // Add LG by name and vec<labelName>?? void RemoveLabelGroup(LabelGroup *labelGroup); LabelGroupSetType GetLabelGroups(); unsigned int GetNumberOfLabelGroups(); unsigned int GetIndexOfLabelGroup(LabelGroup *labelGroup); LabelGroup *GetLabelGroupByName(std::string labelGroup); void SetLabelVisibility(LabelGroup::Label *label, bool isVisible); void SetLabelColor(LabelGroup::Label *label, Color color); void RenameLabel(LabelGroup *labelGroup, LabelGroup::Label *label, std::string newName); void SetLabelForActivatedTubes(LabelGroup *labelGroup, LabelGroup::Label *label); LabelGroup::Label *GetLabelByName(LabelGroup *labelGroup, std::string labelName); 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); ~TubeGraphProperty() override; private: std::vector<TubeDescriptorType> m_ActiveTubes; LabelGroupSetType m_LabelGroups; std::map<TubeToLabelGroupType, std::string> m_TubeToLabelsMap; std::vector<Annotation *> m_Annotations; bool TubeDescriptorsCompare(const TubeDescriptorType &tube1, const TubeDescriptorType &tube2); // purposely not implemented TubeGraphProperty &operator=(const TubeGraphProperty &); bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; itk::LightObject::Pointer InternalClone() const override; }; } // namespace mitk #endif diff --git a/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp b/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp index bbc2f89c39..745be8cc85 100644 --- a/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp +++ b/Modules/TubeGraph/src/Rendering/mitkTubeGraphProperty.cpp @@ -1,428 +1,438 @@ /*============================================================================ 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 "mitkTubeGraphProperty.h" #include <mitkRenderingManager.h> mitk::TubeGraphProperty::TubeGraphProperty() /*: m_LabelGroups(), m_ActiveTubes(), m_TubeToLabelsMap()*/ { } mitk::TubeGraphProperty::TubeGraphProperty(const mitk::TubeGraphProperty &other) : BaseProperty(other), m_ActiveTubes(other.m_ActiveTubes), m_LabelGroups(other.m_LabelGroups), m_TubeToLabelsMap(other.m_TubeToLabelsMap), m_Annotations(other.m_Annotations) { } mitk::TubeGraphProperty::~TubeGraphProperty() { m_ActiveTubes.clear(); m_TubeToLabelsMap.clear(); for (auto it = m_LabelGroups.begin(); it != m_LabelGroups.end(); it++) delete *it; m_LabelGroups.clear(); } bool mitk::TubeGraphProperty::IsTubeVisible(const TubeDescriptorType &tube) { // search for any label settings for the tube if (m_LabelGroups.size() > 0) { for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it).first.first)) { // At the moment only the first entry is considered LabelGroup *lg = this->GetLabelGroupByName((*it).first.second); LabelGroup::Label *label = this->GetLabelByName(lg, (*it).second); return label->isVisible; } } // If nothing is found, look if the first labelgroup is visible for "undefined" label LabelGroup::Label *label = this->GetLabelByName((*m_LabelGroups.begin()), "Undefined"); return label->isVisible; } else return true; } void mitk::TubeGraphProperty::SetTubeActive(const TubeDescriptorType &tube, const bool &active) { // set active if (active) { for (std::vector<TubeDescriptorType>::const_iterator it = m_ActiveTubes.begin(); it != m_ActiveTubes.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it))) { return; } } // if not found, add it m_ActiveTubes.push_back(tube); this->Modified(); } // set deactive else { for (auto it = m_ActiveTubes.begin(); it != m_ActiveTubes.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it))) { // if found, delete it m_ActiveTubes.erase(it); this->Modified(); return; } } } //// render new activation // RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::TubeGraphProperty::SetTubesActive(std::vector<TubeDescriptorType> &tubes) { for (auto it = tubes.begin(); it != tubes.end(); it++) this->SetTubeActive(*it, true); } bool mitk::TubeGraphProperty::IsTubeActive(const TubeDescriptorType &tube) { for (std::vector<TubeDescriptorType>::const_iterator it = m_ActiveTubes.begin(); it != m_ActiveTubes.end(); it++) { if (this->TubeDescriptorsCompare((*it), tube)) return true; } return false; } std::vector<mitk::TubeGraphProperty::TubeDescriptorType> mitk::TubeGraphProperty::GetActiveTubes() { return m_ActiveTubes; } void mitk::TubeGraphProperty::DeactivateAllTubes() { // if (!m_ActiveTubes.empty()) m_ActiveTubes.clear(); this->Modified(); } void mitk::TubeGraphProperty::AddAnnotation(mitk::TubeGraphProperty::Annotation *annotation) { for (auto it = m_Annotations.begin(); it != m_Annotations.end(); it++) { if ((*it)->name == annotation->name) { MITK_INFO << "An Annotation with this name already exists."; return; } } m_Annotations.push_back(annotation); } mitk::TubeGraphProperty::Annotation *mitk::TubeGraphProperty::GetAnnotationByName(std::string annotation) { for (auto it = m_Annotations.begin(); it != m_Annotations.end(); it++) { if ((*it)->name == annotation) return *it; } return nullptr; } std::vector<mitk::TubeGraphProperty::Annotation *> mitk::TubeGraphProperty::GetAnnotations() { return m_Annotations; } void mitk::TubeGraphProperty::RemoveAnnotation(mitk::TubeGraphProperty::Annotation *annotation) { for (auto it = m_Annotations.begin(); it != m_Annotations.end(); it++) { if ((*it)->name == annotation->name) { m_Annotations.erase(it); return; } } } mitk::Color mitk::TubeGraphProperty::GetColorOfTube(const TubeDescriptorType &tube) { Color color; // grey as default color color[0] = 150; color[1] = 150; color[2] = 150; bool isActive(false); bool hasLabel(false); // search the vector with active tubes first. If the tube is active it can not have another color. for (std::vector<TubeDescriptorType>::const_iterator it = m_ActiveTubes.begin(); it != m_ActiveTubes.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it))) { color[0] = 255; color[1] = 255; color[2] = 0; isActive = true; break; } } if (!isActive) { // So let see which label is activ on this tube and which color is set. if (m_LabelGroups.size() > 0) { for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { if (this->TubeDescriptorsCompare(tube, (*it).first.first)) { // At the moment only the first entry is considered LabelGroup *lg = this->GetLabelGroupByName((*it).first.second); LabelGroup::Label *label = this->GetLabelByName(lg, (*it).second); color = label->labelColor; hasLabel = true; } } // If nothing is found, look if the first labelgroup is visible for "undefined" label if (!hasLabel) { LabelGroup::Label *label = this->GetLabelByName(*(m_LabelGroups.begin()), "Undefined"); color = label->labelColor; } } } return color; } void mitk::TubeGraphProperty::SetLabelForActivatedTubes(LabelGroup *labelGroup, LabelGroup::Label *label) { bool isUndefined(label->labelName.compare("Undefined") == 0); for (unsigned int i = 0; i < m_ActiveTubes.size(); i++) { bool isInList(false); for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { if ((this->TubeDescriptorsCompare(it->first.first, m_ActiveTubes[i])) && (labelGroup->labelGroupName.compare(it->first.second) == 0)) { // If tube has a label fromlabel group, deleted if "undefined" is clicked. if (isUndefined) { m_TubeToLabelsMap.erase(it); break; } else { it->second = label->labelName; isInList = true; } } } if (!isInList && !isUndefined) { TubeToLabelGroupType tubeToLabelGroup(m_ActiveTubes[i], labelGroup->labelGroupName); m_TubeToLabelsMap.insert(std::pair<TubeToLabelGroupType, std::string>(tubeToLabelGroup, label->labelName)); } } this->Modified(); m_ActiveTubes.clear(); } void mitk::TubeGraphProperty::SetTubesToLabels(std::map<TubeToLabelGroupType, std::string> tubeToLabelMap) { m_TubeToLabelsMap = tubeToLabelMap; } std::map<mitk::TubeGraphProperty::TubeToLabelGroupType, std::string> mitk::TubeGraphProperty::GetTubesToLabels() { return m_TubeToLabelsMap; } void mitk::TubeGraphProperty::AddLabelGroup(LabelGroup *labelGroup, unsigned int position) { // Check if the name is unique for (auto it = m_LabelGroups.begin(); it != m_LabelGroups.end(); it++) { if (labelGroup->labelGroupName.compare((*it)->labelGroupName) == 0) { MITK_ERROR << "The label group must be unique! This name already exists!"; return; } } // Add the label group at position, if you can, otherwise put it at the end of the vector if (position < m_LabelGroups.size() - 1) { auto it = m_LabelGroups.begin() + position; m_LabelGroups.insert(it, labelGroup); } else { m_LabelGroups.push_back(labelGroup); // when given position is larger then vector size, the label group will be added at the last position if (position >= m_LabelGroups.size()) MITK_INFO << "Position is out of space. So the label group was added on last position: " << m_LabelGroups.size() - 1; } } void mitk::TubeGraphProperty::RemoveLabelGroup(LabelGroup *labelGroup) { // find labelGroup in vector auto foundElement = std::find(m_LabelGroups.begin(), m_LabelGroups.end(), labelGroup); unsigned int pos = foundElement - m_LabelGroups.begin(); // found it? delete it! if (pos < m_LabelGroups.size()) { // delete every assignment to a tube for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { if (labelGroup->labelGroupName.compare((*it).first.second) == 0) m_TubeToLabelsMap.erase(it); } m_LabelGroups.erase(foundElement); } else { MITK_ERROR << "Could not find the label group!"; return; } } void mitk::TubeGraphProperty::SetLabelColor(LabelGroup::Label *label, mitk::Color color) { // LabelGroup? Check if it is a label in property class if (label) { label->labelColor = color; this->Modified(); } } void mitk::TubeGraphProperty::SetLabelVisibility(LabelGroup::Label *label, bool isVisible) { // LabelGroup? Check if it is a label in property class if (label) { label->isVisible = isVisible; this->Modified(); } } void mitk::TubeGraphProperty::RenameLabel(LabelGroup *labelGroup, LabelGroup::Label *label, std::string newName) { // LabelGroup? Check if it is a label in property class if (label) { // rename the label in the assignement vector for tubes for (auto it = m_TubeToLabelsMap.begin(); it != m_TubeToLabelsMap.end(); it++) { // Label group fit? if (labelGroup->labelGroupName.compare((*it).first.second) == 0) // label fit? if (label->labelName.compare((*it).second) == 0) // rename it (*it).second = newName; } // rename the label in label group label->labelName = newName; } } mitk::TubeGraphProperty::LabelGroupSetType mitk::TubeGraphProperty::GetLabelGroups() { return m_LabelGroups; } unsigned int mitk::TubeGraphProperty::GetNumberOfLabelGroups() { return m_LabelGroups.size(); } unsigned int mitk::TubeGraphProperty::GetIndexOfLabelGroup(mitk::TubeGraphProperty::LabelGroup *labelGroup) { unsigned int pos = std::find(m_LabelGroups.begin(), m_LabelGroups.end(), labelGroup) - m_LabelGroups.begin(); if (pos < m_LabelGroups.size()) return pos; // label group is not in property class else { MITK_ERROR << "Could not find such a label group!"; return m_LabelGroups.size(); } } mitk::TubeGraphProperty::LabelGroup *mitk::TubeGraphProperty::GetLabelGroupByName(std::string labelGroup) { for (auto it = m_LabelGroups.begin(); it != m_LabelGroups.end(); it++) { if (labelGroup.compare((*it)->labelGroupName) == 0) return (*it); } MITK_ERROR << "Could not find such a label group!"; return nullptr; } mitk::TubeGraphProperty::LabelGroup::Label *mitk::TubeGraphProperty::GetLabelByName(LabelGroup *labelGroup, std::string labelName) { for (auto it = labelGroup->labels.begin(); it != labelGroup->labels.end(); it++) { if (labelName.compare((*it)->labelName) == 0) return (*it); } MITK_ERROR << "Could not find such a label!"; return nullptr; } bool mitk::TubeGraphProperty::TubeDescriptorsCompare(const TubeDescriptorType &tube1, const TubeDescriptorType &tube2) { if (((tube1.first == tube2.first) && (tube1.second == tube2.second)) || ((tube1.first == tube2.second) && (tube1.second == tube2.first))) return true; else return false; } bool mitk::TubeGraphProperty::IsEqual(const BaseProperty &) const { // TODO see ResectionProposalDescriptorProperty return true; } bool mitk::TubeGraphProperty::Assign(const BaseProperty &) { // TODO see ResectionProposalDescriptorProperty return true; } std::string mitk::TubeGraphProperty::GetValueAsString() const { // TODO return ""; } 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 +} diff --git a/Modules/US/USNavigation/mitkAbstractUltrasoundTrackerDevice.cpp b/Modules/US/USNavigation/mitkAbstractUltrasoundTrackerDevice.cpp index 0f9e821238..b6ced66df9 100644 --- a/Modules/US/USNavigation/mitkAbstractUltrasoundTrackerDevice.cpp +++ b/Modules/US/USNavigation/mitkAbstractUltrasoundTrackerDevice.cpp @@ -1,495 +1,496 @@ /*============================================================================ 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 "mitkAbstractUltrasoundTrackerDevice.h" #include "mitkImageReadAccessor.h" #include "mitkNavigationDataDelayFilter.h" #include "mitkNavigationDataDisplacementFilter.h" #include "mitkNavigationDataSmoothingFilter.h" #include "mitkTrackingDeviceSource.h" +#include <mitkUIDGenerator.h> // US Control Interfaces #include "mitkUSControlInterfaceBMode.h" #include "mitkUSControlInterfaceDoppler.h" #include "mitkUSControlInterfaceProbes.h" // Microservices #include <usGetModuleContext.h> #include <usModule.h> #include <usModuleContext.h> #include <usServiceProperties.h> #include <algorithm> // TempIncludes #include <tinyxml2.h> const std::string mitk::AbstractUltrasoundTrackerDevice::DeviceClassIdentifier = "org.mitk.modules.us.AbstractUltrasoundTrackerDevice"; const char *mitk::AbstractUltrasoundTrackerDevice::DefaultProbeIdentifier = "default"; const char *mitk::AbstractUltrasoundTrackerDevice::ProbeAndDepthSeperator = "_"; const std::string mitk::AbstractUltrasoundTrackerDevice::US_INTERFACE_NAME = "org.mitk.services.AbstractUltrasoundTrackerDevice"; const std::string mitk::AbstractUltrasoundTrackerDevice::US_PROPKEY_DEVICENAME = US_INTERFACE_NAME + ".devicename"; const std::string mitk::AbstractUltrasoundTrackerDevice::US_PROPKEY_CLASS = US_INTERFACE_NAME + ".class"; const std::string mitk::AbstractUltrasoundTrackerDevice::US_PROPKEY_ID = US_INTERFACE_NAME + ".id"; //____ mitk::AbstractUltrasoundTrackerDevice::AbstractUltrasoundTrackerDevice(USDevice::Pointer usDevice, NavigationDataSource::Pointer trackingDevice, bool trackedUltrasoundActive) : m_UltrasoundDevice(usDevice), m_TrackingDeviceDataSource(trackingDevice), m_SmoothingFilter(mitk::NavigationDataSmoothingFilter::New()), m_DelayFilter(mitk::NavigationDataDelayFilter::New(0)), m_DisplacementFilter(mitk::NavigationDataDisplacementFilter::New()), m_LastFilterOfIGTPipeline(nullptr), m_NumberOfSmoothingValues(0), m_DelayCount(0), m_IsTrackedUltrasoundActive(trackedUltrasoundActive) { m_DisplacementFilter->SetTransform6DOF(true); this->RebuildFilterPipeline(); // create a new output (for the image data) //___ mitk::Image::Pointer newOutput = mitk::Image::New(); //___ this->SetNthOutput(0, newOutput); // Combined Modality should not spawn an own acquire thread, because // image acquiring is done by the included us device //___ m_UltrasoundDevice->SetSpawnAcquireThread(false); } mitk::AffineTransform3D::Pointer mitk::AbstractUltrasoundTrackerDevice::GetUSPlaneTransform() { return mitk::AffineTransform3D::New(); } void mitk::AbstractUltrasoundTrackerDevice::SetIsFreezed(bool freeze) { if (m_UltrasoundDevice.IsNull() || m_TrackingDeviceDataSource.IsNull()) { MITK_WARN << "Combined modality not correctly initialized, aborting!"; return; } if (!m_UltrasoundDevice->GetIsActive()) { MITK_WARN("mitkUSDevice") << "Cannot freeze or unfreeze if device is not active."; return; } this->OnFreeze(freeze); if (freeze) { m_IsFreezed = true; } else { m_IsFreezed = false; } } bool mitk::AbstractUltrasoundTrackerDevice::GetIsFreezed() { return m_IsFreezed; } mitk::AbstractUltrasoundTrackerDevice::~AbstractUltrasoundTrackerDevice() { if (m_ServiceRegistration != nullptr) { m_ServiceRegistration.Unregister(); } m_ServiceRegistration = 0; } mitk::AffineTransform3D::Pointer mitk::AbstractUltrasoundTrackerDevice::GetCalibration() { return this->GetCalibration(this->GetCurrentDepthValue(), this->GetIdentifierForCurrentProbe()); } mitk::AffineTransform3D::Pointer mitk::AbstractUltrasoundTrackerDevice::GetCalibration(std::string depth) { return this->GetCalibration(depth, this->GetIdentifierForCurrentProbe()); } mitk::AffineTransform3D::Pointer mitk::AbstractUltrasoundTrackerDevice::GetCalibration(std::string depth, std::string probe) { // make sure that there is no '/' which would cause problems for TinyXML std::replace(probe.begin(), probe.end(), '/', '-'); // create identifier for calibration from probe and depth std::string calibrationKey = probe + mitk::AbstractUltrasoundTrackerDevice::ProbeAndDepthSeperator + depth; // find calibration for combination of probe identifier and depth std::map<std::string, mitk::AffineTransform3D::Pointer>::iterator calibrationIterator = m_Calibrations.find(calibrationKey); if (calibrationIterator == m_Calibrations.end()) { return nullptr; } return calibrationIterator->second; } void mitk::AbstractUltrasoundTrackerDevice::SetCalibration(mitk::AffineTransform3D::Pointer calibration) { if (calibration.IsNull()) { MITK_WARN << "Null pointer passed to SetCalibration of mitk::USDevice. Ignoring call."; return; } std::string calibrationKey = this->GetIdentifierForCurrentCalibration(); if (calibrationKey.empty()) { MITK_WARN << "Could not get a key for the calibration -> Calibration cannot be set."; return; } m_Calibrations[calibrationKey] = calibration; } bool mitk::AbstractUltrasoundTrackerDevice::RemoveCalibration() { return this->RemoveCalibration(this->GetCurrentDepthValue(), this->GetIdentifierForCurrentProbe()); } bool mitk::AbstractUltrasoundTrackerDevice::RemoveCalibration(std::string depth) { return this->RemoveCalibration(depth, this->GetIdentifierForCurrentProbe()); } bool mitk::AbstractUltrasoundTrackerDevice::RemoveCalibration(std::string depth, std::string probe) { // make sure that there is no '/' which would cause problems for TinyXML std::replace(probe.begin(), probe.end(), '/', '-'); // create identifier for calibration from probe and depth std::string calibrationKey = probe + mitk::AbstractUltrasoundTrackerDevice::ProbeAndDepthSeperator + depth; return m_Calibrations.erase(calibrationKey) > 0; } std::string mitk::AbstractUltrasoundTrackerDevice::GetDeviceClass() { return DeviceClassIdentifier; } mitk::USImageSource::Pointer mitk::AbstractUltrasoundTrackerDevice::GetUSImageSource() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("AbstractUltrasoundTrackerDevice")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetUSImageSource(); } mitk::NavigationDataSource::Pointer mitk::AbstractUltrasoundTrackerDevice::GetNavigationDataSource() { if (m_LastFilterOfIGTPipeline.IsNull()) { this->RebuildFilterPipeline(); } m_LastFilterOfIGTPipeline->SetToolMetaDataCollection(this->m_TrackingDeviceDataSource->GetToolMetaDataCollection()); return m_LastFilterOfIGTPipeline; } bool mitk::AbstractUltrasoundTrackerDevice::GetIsCalibratedForCurrentStatus() { return m_Calibrations.find(this->GetIdentifierForCurrentCalibration()) != m_Calibrations.end(); } bool mitk::AbstractUltrasoundTrackerDevice::GetContainsAtLeastOneCalibration() { return !m_Calibrations.empty(); } std::string mitk::AbstractUltrasoundTrackerDevice::SerializeCalibration() { tinyxml2::XMLDocument doc; auto* calibrations = doc.NewElement("calibrations"); doc.InsertEndChild(calibrations); // For each calibration in the set for (std::map<std::string, mitk::AffineTransform3D::Pointer>::iterator it = m_Calibrations.begin(); it != m_Calibrations.end(); it++) { mitk::AffineTransform3D::MatrixType matrix = it->second->GetMatrix(); mitk::AffineTransform3D::TranslationType translation = it->second->GetTranslation(); auto *elem = doc.NewElement(it->first.c_str()); // Serialize Matrix elem->SetAttribute("M00", matrix[0][0]); elem->SetAttribute("M01", matrix[0][1]); elem->SetAttribute("M02", matrix[0][2]); elem->SetAttribute("M10", matrix[1][0]); elem->SetAttribute("M11", matrix[1][1]); elem->SetAttribute("M12", matrix[1][2]); elem->SetAttribute("M20", matrix[2][0]); elem->SetAttribute("M21", matrix[2][1]); elem->SetAttribute("M22", matrix[2][2]); // Serialize Offset elem->SetAttribute("T0", translation[0]); elem->SetAttribute("T1", translation[1]); elem->SetAttribute("T2", translation[2]); calibrations->InsertEndChild(elem); } tinyxml2::XMLPrinter printer; doc.Print(&printer); return printer.CStr(); } void mitk::AbstractUltrasoundTrackerDevice::DeserializeCalibration(const std::string &xmlString, bool clearPreviousCalibrations) { // Sanitize Input if (xmlString == "") { MITK_ERROR << "Empty string passed to Deserialize() method of CombinedModality. Aborting..."; mitkThrow() << "Empty string passed to Deserialize() method of CombinedModality. Aborting..."; return; } // Clear previous calibrations if necessary if (clearPreviousCalibrations) m_Calibrations.clear(); // Parse Input tinyxml2::XMLDocument doc; if (tinyxml2::XML_SUCCESS != doc.Parse(xmlString.c_str())) { MITK_ERROR << "Unable to deserialize calibrations in CombinedModality. Error was: " << doc.ErrorStr(); mitkThrow() << "Unable to deserialize calibrations in CombinedModality. Error was: " << doc.ErrorStr(); return; } auto *root = doc.FirstChildElement(); if (root == nullptr) { MITK_ERROR << "Unable to deserialize calibrations in CombinedModality. String contained no root element."; mitkThrow() << "Unable to deserialize calibrations in CombinedModality. String contained no root element."; return; } // Read Calibrations for (auto *elem = root->FirstChildElement(); elem != nullptr; elem = elem->NextSiblingElement()) { mitk::AffineTransform3D::MatrixType matrix; mitk::AffineTransform3D::OffsetType translation; std::string calibName = elem->Value(); // Deserialize Matrix elem->QueryDoubleAttribute("M00", &matrix[0][0]); elem->QueryDoubleAttribute("M01", &matrix[0][1]); elem->QueryDoubleAttribute("M02", &matrix[0][2]); elem->QueryDoubleAttribute("M10", &matrix[1][0]); elem->QueryDoubleAttribute("M11", &matrix[1][1]); elem->QueryDoubleAttribute("M12", &matrix[1][2]); elem->QueryDoubleAttribute("M20", &matrix[2][0]); elem->QueryDoubleAttribute("M21", &matrix[2][1]); elem->QueryDoubleAttribute("M22", &matrix[2][2]); // Deserialize Offset elem->QueryDoubleAttribute("T0", &translation[0]); elem->QueryDoubleAttribute("T1", &translation[1]); elem->QueryDoubleAttribute("T2", &translation[2]); mitk::AffineTransform3D::Pointer calibration = mitk::AffineTransform3D::New(); calibration->SetMatrix(matrix); calibration->SetTranslation(translation); m_Calibrations[calibName] = calibration; } } void mitk::AbstractUltrasoundTrackerDevice::SetNumberOfSmoothingValues(unsigned int numberOfSmoothingValues) { unsigned int oldNumber = m_NumberOfSmoothingValues; m_NumberOfSmoothingValues = numberOfSmoothingValues; // if filter should be activated or deactivated if ((oldNumber == 0 && numberOfSmoothingValues != 0) || (oldNumber != 0 && numberOfSmoothingValues == 0)) { this->RebuildFilterPipeline(); } m_SmoothingFilter->SetNumerOfValues(numberOfSmoothingValues); } void mitk::AbstractUltrasoundTrackerDevice::SetDelayCount(unsigned int delayCount) { unsigned int oldCount = m_DelayCount; m_DelayCount = delayCount; // if filter should be activated or deactivated if ((oldCount == 0 && delayCount != 0) || (oldCount != 0 && delayCount == 0)) { this->RebuildFilterPipeline(); } m_DelayFilter->SetDelay(delayCount); } void mitk::AbstractUltrasoundTrackerDevice::GenerateData() {} std::string mitk::AbstractUltrasoundTrackerDevice::GetIdentifierForCurrentCalibration() { return this->GetIdentifierForCurrentProbe() + mitk::AbstractUltrasoundTrackerDevice::ProbeAndDepthSeperator + this->GetCurrentDepthValue(); } std::string mitk::AbstractUltrasoundTrackerDevice::GetIdentifierForCurrentProbe() { us::ServiceProperties usdeviceProperties = m_UltrasoundDevice->GetServiceProperties(); us::ServiceProperties::const_iterator probeIt = usdeviceProperties.find(mitk::USDevice::GetPropertyKeys().US_PROPKEY_PROBES_SELECTED); // get probe identifier from control interface for probes std::string probeName = mitk::AbstractUltrasoundTrackerDevice::DefaultProbeIdentifier; if (probeIt != usdeviceProperties.end()) { probeName = (probeIt->second).ToString(); } // make sure that there is no '/' which would cause problems for TinyXML std::replace(probeName.begin(), probeName.end(), '/', '-'); return probeName; } std::string mitk::AbstractUltrasoundTrackerDevice::GetCurrentDepthValue() { us::ServiceProperties usdeviceProperties = m_UltrasoundDevice->GetServiceProperties(); // get string for depth value from the micro service properties std::string depth; us::ServiceProperties::iterator depthIterator = usdeviceProperties.find(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH); if (depthIterator != usdeviceProperties.end()) { depth = depthIterator->second.ToString(); } else { depth = "0"; } return depth; } void mitk::AbstractUltrasoundTrackerDevice::RebuildFilterPipeline() { m_LastFilterOfIGTPipeline = m_TrackingDeviceDataSource; if (m_NumberOfSmoothingValues > 0) { m_SmoothingFilter->ConnectTo(m_LastFilterOfIGTPipeline.GetPointer()); m_LastFilterOfIGTPipeline = m_SmoothingFilter; } if (m_DelayCount > 0) { m_DelayFilter->ConnectTo(m_LastFilterOfIGTPipeline.GetPointer()); m_LastFilterOfIGTPipeline = m_DelayFilter; } if (m_IsTrackedUltrasoundActive) { m_DisplacementFilter->ConnectTo(m_LastFilterOfIGTPipeline.GetPointer()); m_LastFilterOfIGTPipeline = m_DisplacementFilter; } } void mitk::AbstractUltrasoundTrackerDevice::UnregisterOnService() { if (m_UltrasoundDevice->GetDeviceState() == USDevice::State_Activated) { m_UltrasoundDevice->Deactivate(); } if (m_UltrasoundDevice->GetDeviceState() == USDevice::State_Connected) { m_UltrasoundDevice->Disconnect(); } if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } void mitk::AbstractUltrasoundTrackerDevice::RegisterAsMicroservice() { // Get Context us::ModuleContext *context = us::GetModuleContext(); // Define ServiceProps // us::ServiceProperties props; mitk::UIDGenerator uidGen = mitk::UIDGenerator("org.mitk.services.AbstractUltrasoundTrackerDevice"); m_ServiceProperties[US_PROPKEY_ID] = uidGen.GetUID(); m_ServiceProperties[US_PROPKEY_DEVICENAME] = m_UltrasoundDevice->GetName(); m_ServiceProperties[US_PROPKEY_CLASS] = mitk::AbstractUltrasoundTrackerDevice::DeviceClassIdentifier; m_ServiceRegistration = context->RegisterService(this, m_ServiceProperties); } mitk::USAbstractControlInterface::Pointer mitk::AbstractUltrasoundTrackerDevice::GetControlInterfaceCustom() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetControlInterfaceCustom(); } mitk::USControlInterfaceBMode::Pointer mitk::AbstractUltrasoundTrackerDevice::GetControlInterfaceBMode() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetControlInterfaceBMode(); } mitk::USControlInterfaceProbes::Pointer mitk::AbstractUltrasoundTrackerDevice::GetControlInterfaceProbes() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetControlInterfaceProbes(); } mitk::USControlInterfaceDoppler::Pointer mitk::AbstractUltrasoundTrackerDevice::GetControlInterfaceDoppler() { if (m_UltrasoundDevice.IsNull()) { MITK_ERROR("USCombinedModality")("USDevice") << "UltrasoundDevice must not be null."; mitkThrow() << "UltrasoundDevice must not be null."; } return m_UltrasoundDevice->GetControlInterfaceDoppler(); } diff --git a/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt b/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt index 4d2a1eebde..c6772ccead 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.imagecropper/CMakeLists.txt @@ -1,8 +1,8 @@ project(org_mitk_gui_qt_imagecropper) mitk_create_plugin( EXPORT_DIRECTIVE MITK_QT_IMAGECROPPER EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDS MitkQtWidgetsExt MitkBoundingShape MitkMultilabel + MODULE_DEPENDS MitkQtWidgetsExt MitkBoundingShape MitkMultilabel MitkROI ) 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 91a2f08bc4..427ed3baf6 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox +++ b/Plugins/org.mitk.gui.qt.imagecropper/documentation/UserManual/QmitkImageCropper.dox @@ -1,52 +1,52 @@ /** \page org_mitk_views_imagecropper The Image Cropper \imageMacro{imagecropper-dox.svg,"Icon of the Image Cropper Plugin.",20} \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. \section org_mitk_views_imagecropperAdvanced Advanced settings In the advanced settings view you find additional features: \imageMacro{Advanced_ImageCropperView.png,"Advanced Settings.",7.09} \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 By enabling this checkbox the image is replaced by the cropped subvolume. Be careful to use this option since there is no undo action available. \subsection org_mitk_views_imagecropperAdvancedTimestep Crop current time step only If you have an xD + t image, the whole time series (all timesteps) is cropped by default. In this case, the 'time geometry' of the current time step is used. If the checkbox 'Only crop current time step' is ticked, the xD + t image is reduced to an xD image (e.g., 3D+t --> 3D) with the current time step only. That can be useful if you want to extract a single image or its corresponding subvolume of the time series. \section org_mitk_views_imagecropperIssues Current issues 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/files.cmake b/Plugins/org.mitk.gui.qt.imagecropper/files.cmake index 59435c5859..58e89a3b01 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/files.cmake +++ b/Plugins/org.mitk.gui.qt.imagecropper/files.cmake @@ -1,32 +1,34 @@ set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkImageCropperView.cpp + QmitkConvertGeometryDataToROIAction.cpp ) set(UI_FILES src/internal/QmitkImageCropperViewControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkImageCropperView.h + src/internal/QmitkConvertGeometryDataToROIAction.h ) set(CACHED_RESOURCE_FILES resources/crop.svg plugin.xml ) set(QRC_FILES resources/imagecropper.qrc ) set(CPP_FILES) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) foreach(file ${INTERNAL_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/internal/${file}) endforeach(file ${INTERNAL_CPP_FILES}) diff --git a/Plugins/org.mitk.gui.qt.imagecropper/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.imagecropper/manifest_headers.cmake index 8211a4fb19..dc360f2d35 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.imagecropper/manifest_headers.cmake @@ -1,5 +1,5 @@ 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 ccc04b49d1..3974b6148f 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml +++ b/Plugins/org.mitk.gui.qt.imagecropper/plugin.xml @@ -1,27 +1,31 @@ <?xml version="1.0" encoding="UTF-8"?> <?eclipse version="3.0"?> <plugin> <extension point="org.blueberry.ui.views"> <view id="org.mitk.views.imagecropper" name="Image Cropper" category="General" icon="resources/crop.svg" class="QmitkImageCropperView" > <description>Crop images to a given size</description> <keywordReference id="org.mitk.views.imagecropper.ViewKeyword"/> </view> </extension> + <extension point="org.mitk.gui.qt.datamanager.contextMenuActions"> + <contextMenuAction nodeDescriptorName="GeometryData" label="Convert to ROI" icon="" class="QmitkConvertGeometryDataToROIAction" /> + </extension> + <extension point="org.blueberry.ui.keywords"> <keyword id="org.mitk.views.imagecropper.ViewKeyword" label="Size" /> <keyword id="org.mitk.views.imagecropper.ViewKeyword" label="Match Size" /> <keyword id="org.mitk.views.imagecropper.ViewKeyword" label="Resizing" /> <keyword id="org.mitk.views.imagecropper.ViewKeyword" label="Changing Dimension" /> <keyword id="org.mitk.views.imagecropper.ViewKeyword" label="Image Dimension" /> <keyword id="org.mitk.views.imagecropper.ViewKeyword" label="Image cropping" /> </extension> </plugin> 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 0000000000..5dd3020546 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkConvertGeometryDataToROIAction.cpp @@ -0,0 +1,154 @@ +/*============================================================================ + +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 <mitkGeometryData.h> +#include <mitkImage.h> +#include <mitkNodePredicateDataType.h> +#include <mitkROI.h> + +#include <QMessageBox> + +namespace +{ + void handleInvalidNodeSelection() + { + 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); + } + + bool isRotated(const mitk::BaseGeometry* geometry) + { + const auto* matrix = geometry->GetVtkMatrix(); + + 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; + } + } + + return false; + } + + std::pair<std::vector<const mitk::DataNode*>, mitk::DataNode*> getValidInput(const QList<mitk::DataNode::Pointer>& selectedNodes, const mitk::DataStorage* dataStorage) + { + std::pair<std::vector<const mitk::DataNode*>, 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<mitk::GeometryData*>(node->GetData()) != nullptr; + }); + + for (auto node : result.first) + { + auto sourceNodes = dataStorage->GetSources(node, mitk::TNodePredicateDataType<mitk::Image>::New()); + + if (sourceNodes->size() != 1) + mitkThrow(); + + if (result.second == nullptr) + { + if (isRotated(sourceNodes->front()->GetData()->GetGeometry())) + mitkThrow(); + + result.second = sourceNodes->front(); + } + else if (result.second != sourceNodes->front()) + { + mitkThrow(); + } + } + + return result; + } +} + +QmitkConvertGeometryDataToROIAction::QmitkConvertGeometryDataToROIAction() +{ +} + +QmitkConvertGeometryDataToROIAction::~QmitkConvertGeometryDataToROIAction() +{ +} + +void QmitkConvertGeometryDataToROIAction::Run(const QList<mitk::DataNode::Pointer>& selectedNodes) +{ + try + { + auto [nodes, referenceNode] = getValidInput(selectedNodes, m_DataStorage); + + auto roi = mitk::ROI::New(); + roi->SetClonedGeometry(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&) + { + 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 0000000000..8797841a76 --- /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 <mitkIContextMenuAction.h> + +class QmitkConvertGeometryDataToROIAction : public QObject, public mitk::IContextMenuAction +{ + Q_OBJECT + Q_INTERFACES(mitk::IContextMenuAction) + +public: + QmitkConvertGeometryDataToROIAction(); + ~QmitkConvertGeometryDataToROIAction() override; + + void Run(const QList<mitk::DataNode::Pointer>& 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/QmitkImageCropperView.cpp b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp index 6d4155a084..c8ae768dbd 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp @@ -1,512 +1,510 @@ /*============================================================================ 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 "QmitkImageCropperView.h" #include <mitkBoundingShapeCropper.h> #include <mitkImageStatisticsHolder.h> #include <mitkInteractionConst.h> #include <mitkITKImageImport.h> #include <mitkLabelSetImage.h> #include <mitkNodePredicateDataType.h> #include <mitkNodePredicateAnd.h> #include <mitkNodePredicateNot.h> #include <mitkNodePredicateProperty.h> #include <mitkNodePredicateFunction.h> #include <mitkRenderingManager.h> #include <usModuleRegistry.h> #include <QMessageBox> const std::string QmitkImageCropperView::VIEW_ID = "org.mitk.views.qmitkimagecropper"; QmitkImageCropperView::QmitkImageCropperView(QObject *) : m_ParentWidget(nullptr) , m_BoundingShapeInteractor(nullptr) , m_CropOutsideValue(0) { CreateBoundingShapeInteractor(false); } QmitkImageCropperView::~QmitkImageCropperView() { //disable interactor if (m_BoundingShapeInteractor != nullptr) { m_BoundingShapeInteractor->SetDataNode(nullptr); m_BoundingShapeInteractor->EnableInteraction(false); } } void QmitkImageCropperView::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.imageSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls.imageSelectionWidget->SetNodePredicate( mitk::NodePredicateAnd::New(mitk::TNodePredicateDataType<mitk::Image>::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_Controls.imageSelectionWidget->SetSelectionIsOptional(true); m_Controls.imageSelectionWidget->SetAutoSelectNewNodes(true); m_Controls.imageSelectionWidget->SetEmptyInfo(QString("Please select an image node")); m_Controls.imageSelectionWidget->SetPopUpTitel(QString("Select image node")); connect(m_Controls.imageSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkImageCropperView::OnImageSelectionChanged); m_Controls.boundingBoxSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls.boundingBoxSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType<mitk::GeometryData>::New(), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_Controls.boundingBoxSelectionWidget->SetSelectionIsOptional(true); m_Controls.boundingBoxSelectionWidget->SetAutoSelectNewNodes(true); m_Controls.boundingBoxSelectionWidget->SetEmptyInfo(QString("Please select a bounding box")); m_Controls.boundingBoxSelectionWidget->SetPopUpTitel(QString("Select bounding box node")); connect(m_Controls.boundingBoxSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkImageCropperView::OnBoundingBoxSelectionChanged); connect(m_Controls.buttonCreateNewBoundingBox, SIGNAL(clicked()), this, SLOT(OnCreateNewBoundingBox())); connect(m_Controls.buttonCropping, SIGNAL(clicked()), this, SLOT(OnCropping())); connect(m_Controls.buttonMasking, SIGNAL(clicked()), this, SLOT(OnMasking())); auto lambda = [this]() { m_Controls.groupImageSettings->setVisible(!m_Controls.groupImageSettings->isVisible()); }; connect(m_Controls.buttonAdvancedSettings, &ctkExpandButton::clicked, this, lambda); connect(m_Controls.spinBoxOutsidePixelValue, SIGNAL(valueChanged(int)), this, SLOT(OnSliderValueChanged(int))); SetDefaultGUI(); m_ParentWidget = parent; this->OnImageSelectionChanged(m_Controls.imageSelectionWidget->GetSelectedNodes()); this->OnBoundingBoxSelectionChanged(m_Controls.boundingBoxSelectionWidget->GetSelectedNodes()); } void QmitkImageCropperView::OnImageSelectionChanged(QList<mitk::DataNode::Pointer>) { bool rotationEnabled = false; m_Controls.labelWarningRotation->setVisible(false); auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { SetDefaultGUI(); return; } auto image = dynamic_cast<mitk::Image*>(imageNode->GetData()); if (nullptr != image) { if (image->GetDimension() < 3) { QMessageBox::warning(nullptr, tr("Invalid image selected"), tr("ImageCropper only works with 3 or more dimensions."), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); SetDefaultGUI(); return; } m_ParentWidget->setEnabled(true); m_Controls.buttonCreateNewBoundingBox->setEnabled(true); vtkSmartPointer<vtkMatrix4x4> imageMat = image->GetGeometry()->GetVtkMatrix(); // check whether the image geometry is rotated; if so, no pixel aligned cropping or masking can be performed if ((imageMat->GetElement(1, 0) == 0.0) && (imageMat->GetElement(0, 1) == 0.0) && (imageMat->GetElement(1, 2) == 0.0) && (imageMat->GetElement(2, 1) == 0.0) && (imageMat->GetElement(2, 0) == 0.0) && (imageMat->GetElement(0, 2) == 0.0)) { rotationEnabled = false; m_Controls.labelWarningRotation->setVisible(false); } else { rotationEnabled = true; m_Controls.labelWarningRotation->setStyleSheet(" QLabel { color: rgb(255, 0, 0) }"); m_Controls.labelWarningRotation->setVisible(true); } this->CreateBoundingShapeInteractor(rotationEnabled); if (itk::IOPixelEnum::SCALAR == image->GetPixelType().GetPixelType()) { // Might be changed with the upcoming new image statistics plugin //(recomputation might be very expensive for large images ;) ) auto statistics = image->GetStatistics(); auto minPixelValue = statistics->GetScalarValueMin(); auto maxPixelValue = statistics->GetScalarValueMax(); if (minPixelValue < std::numeric_limits<int>::min()) { minPixelValue = std::numeric_limits<int>::min(); } if (maxPixelValue > std::numeric_limits<int>::max()) { maxPixelValue = std::numeric_limits<int>::max(); } m_Controls.spinBoxOutsidePixelValue->setEnabled(true); m_Controls.spinBoxOutsidePixelValue->setMaximum(static_cast<int>(maxPixelValue)); m_Controls.spinBoxOutsidePixelValue->setMinimum(static_cast<int>(minPixelValue)); m_Controls.spinBoxOutsidePixelValue->setValue(static_cast<int>(minPixelValue)); } else { m_Controls.spinBoxOutsidePixelValue->setEnabled(false); } unsigned int dim = image->GetDimension(); if (dim < 2 || dim > 4) { m_ParentWidget->setEnabled(false); } if (m_Controls.boundingBoxSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.buttonAdvancedSettings->setEnabled(true); m_Controls.groupImageSettings->setEnabled(true); } } } void QmitkImageCropperView::OnBoundingBoxSelectionChanged(QList<mitk::DataNode::Pointer>) { auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode(); if (boundingBoxNode.IsNull()) { SetDefaultGUI(); m_BoundingShapeInteractor->EnableInteraction(false); m_BoundingShapeInteractor->SetDataNode(nullptr); if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCreateNewBoundingBox->setEnabled(true); } return; } auto boundingBox = dynamic_cast<mitk::GeometryData*>(boundingBoxNode->GetData()); if (nullptr != boundingBox) { // node newly selected boundingBoxNode->SetVisibility(true); m_BoundingShapeInteractor->EnableInteraction(true); m_BoundingShapeInteractor->SetDataNode(boundingBoxNode); mitk::RenderingManager::GetInstance()->InitializeViews(); if (m_Controls.imageSelectionWidget->GetSelectedNode().IsNotNull()) { m_Controls.buttonCropping->setEnabled(true); m_Controls.buttonMasking->setEnabled(true); m_Controls.buttonAdvancedSettings->setEnabled(true); m_Controls.groupImageSettings->setEnabled(true); } } } void QmitkImageCropperView::OnCreateNewBoundingBox() { auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { return; } if (nullptr == imageNode->GetData()) { 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) { return 0 == node->GetName().compare(name.toStdString()); })); if (nullptr != boundingShape) { name = this->AdaptBoundingObjectName(name); } // get current timestep to support 3d+t images auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); const mitk::TimePointType timePoint = renderWindowPart->GetSelectedTimePoint(); const auto imageGeometry = imageNode->GetData()->GetTimeGeometry()->GetGeometryForTimePoint(timePoint); auto boundingBox = mitk::GeometryData::New(); boundingBox->SetGeometry(static_cast<mitk::Geometry3D*>(this->InitializeWithImageGeometry(imageGeometry))); 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("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)) { GetDataStorage()->Add(boundingBoxNode, imageNode); } m_Controls.boundingBoxSelectionWidget->SetCurrentSelectedNode(boundingBoxNode); } void QmitkImageCropperView::OnCropping() { this->ProcessImage(false); } void QmitkImageCropperView::OnMasking() { this->ProcessImage(true); } void QmitkImageCropperView::OnSliderValueChanged(int slidervalue) { m_CropOutsideValue = slidervalue; } void QmitkImageCropperView::CreateBoundingShapeInteractor(bool rotationEnabled) { if (m_BoundingShapeInteractor.IsNull()) { m_BoundingShapeInteractor = mitk::BoundingShapeInteractor::New(); m_BoundingShapeInteractor->LoadStateMachine("BoundingShapeInteraction.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); m_BoundingShapeInteractor->SetEventConfig("BoundingShapeMouseConfig.xml", us::ModuleRegistry::GetModule("MitkBoundingShape")); } m_BoundingShapeInteractor->SetRotationEnabled(rotationEnabled); } mitk::Geometry3D::Pointer QmitkImageCropperView::InitializeWithImageGeometry(const mitk::BaseGeometry* geometry) const { // convert a BaseGeometry into a Geometry3D (otherwise IO is not working properly) if (geometry == nullptr) mitkThrow() << "Geometry is not valid."; auto boundingGeometry = mitk::Geometry3D::New(); boundingGeometry->SetBounds(geometry->GetBounds()); boundingGeometry->SetImageGeometry(geometry->GetImageGeometry()); boundingGeometry->SetOrigin(geometry->GetOrigin()); boundingGeometry->SetSpacing(geometry->GetSpacing()); boundingGeometry->SetIndexToWorldTransform(geometry->GetIndexToWorldTransform()->Clone()); boundingGeometry->Modified(); return boundingGeometry; } void QmitkImageCropperView::ProcessImage(bool mask) { auto renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::IRenderWindowPartStrategy::OPEN); const auto timePoint = renderWindowPart->GetSelectedTimePoint(); auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); return; } auto boundingBoxNode = m_Controls.boundingBoxSelectionWidget->GetSelectedNode(); if (boundingBoxNode.IsNull()) { QMessageBox::information(nullptr, "Warning", "Please load and select a cropping object before starting image processing."); return; } if (!imageNode->GetData()->GetTimeGeometry()->IsValidTimePoint(timePoint)) { QMessageBox::information(nullptr, "Warning", "Please select a time point that is within the time bounds of the selected image."); return; } const auto timeStep = imageNode->GetData()->GetTimeGeometry()->TimePointToTimeStep(timePoint); auto image = dynamic_cast<mitk::Image*>(imageNode->GetData()); auto boundingBox = dynamic_cast<mitk::GeometryData*>(boundingBoxNode->GetData()); if (nullptr != image && nullptr != boundingBox) { // Check if initial node name is already in box name std::string imagePrefix = ""; if (boundingBoxNode->GetName().find(imageNode->GetName()) != 0) { imagePrefix = imageNode->GetName() + "_"; } QString imageName; if (mask) { imageName = QString::fromStdString(imagePrefix + boundingBoxNode->GetName() + "_masked"); } else { imageName = QString::fromStdString(imagePrefix + boundingBoxNode->GetName() + "_cropped"); } if (m_Controls.checkBoxCropTimeStepOnly->isChecked()) { imageName = imageName + "_T" + QString::number(timeStep); } // image and bounding shape ok, set as input auto croppedImageNode = mitk::DataNode::New(); auto cutter = mitk::BoundingShapeCropper::New(); cutter->SetGeometry(boundingBox); // adjustable in advanced settings cutter->SetUseWholeInputRegion(mask); //either mask (mask=true) or crop (mask=false) cutter->SetOutsideValue(m_CropOutsideValue); cutter->SetUseCropTimeStepOnly(m_Controls.checkBoxCropTimeStepOnly->isChecked()); cutter->SetCurrentTimeStep(timeStep); // TODO: Add support for MultiLayer (right now only Mulitlabel support) auto labelsetImageInput = dynamic_cast<mitk::LabelSetImage*>(image); if (nullptr != labelsetImageInput) { cutter->SetInput(labelsetImageInput); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } auto labelSetImage = mitk::LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(cutter->GetOutput()); for (unsigned int i = 0; i < labelsetImageInput->GetNumberOfLayers(); i++) { labelSetImage->AddLabelSetToLayer(i, labelsetImageInput->GetLabelSet(i)); } croppedImageNode->SetData(labelSetImage); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { if (!this->GetDataStorage()->Exists(croppedImageNode)) { this->GetDataStorage()->Add(croppedImageNode, imageNode); } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { imageNode->SetData(labelSetImage); imageNode->Modified(); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, imageNode); // initialize the views to the bounding geometry auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } } else { cutter->SetInput(image); // do the actual cutting try { cutter->Update(); } catch (const itk::ExceptionObject& e) { std::string message = std::string("The Cropping filter could not process because of: \n ") + e.GetDescription(); QMessageBox::warning(nullptr, tr("Cropping not possible!"), tr(message.c_str()), QMessageBox::Ok, QMessageBox::NoButton, QMessageBox::NoButton); return; } //add cropping result to the current data storage as child node to the image node if (!m_Controls.checkOverwriteImage->isChecked()) { croppedImageNode->SetData(cutter->GetOutput()); croppedImageNode->SetProperty("name", mitk::StringProperty::New(imageName.toStdString())); croppedImageNode->SetProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0)); mitk::LevelWindow levelWindow; imageNode->GetLevelWindow(levelWindow); croppedImageNode->SetLevelWindow(levelWindow); if (!this->GetDataStorage()->Exists(croppedImageNode)) { this->GetDataStorage()->Add(croppedImageNode, imageNode); imageNode->SetVisibility(mask); // Give the user a visual clue that something happened when image was cropped } } else // original image will be overwritten by the result image and the bounding box of the result is adjusted { mitk::LevelWindow levelWindow; imageNode->GetLevelWindow(levelWindow); imageNode->SetData(cutter->GetOutput()); imageNode->SetLevelWindow(levelWindow); // Adjust coordinate system by doing a reinit on auto tempDataStorage = mitk::DataStorage::SetOfObjects::New(); tempDataStorage->InsertElement(0, imageNode); // initialize the views to the bounding geometry auto bounds = this->GetDataStorage()->ComputeBoundingGeometry3D(tempDataStorage); mitk::RenderingManager::GetInstance()->InitializeViews(bounds); } } } else { QMessageBox::information(nullptr, "Warning", "Please load and select an image before starting image processing."); } } void QmitkImageCropperView::SetDefaultGUI() { m_Controls.buttonCreateNewBoundingBox->setEnabled(false); m_Controls.buttonCropping->setEnabled(false); m_Controls.buttonMasking->setEnabled(false); m_Controls.buttonAdvancedSettings->setEnabled(false); m_Controls.groupImageSettings->setEnabled(false); m_Controls.groupImageSettings->setVisible(false); m_Controls.checkOverwriteImage->setChecked(false); m_Controls.checkBoxCropTimeStepOnly->setChecked(false); } QString QmitkImageCropperView::AdaptBoundingObjectName(const QString& name) const { unsigned int counter = 2; QString newName = QString("%1 %2").arg(name).arg(counter); while (nullptr != this->GetDataStorage()->GetNode(mitk::NodePredicateFunction::New([&newName](const mitk::DataNode *node) { return 0 == node->GetName().compare(newName.toStdString()); }))) { newName = QString("%1 %2").arg(name).arg(++counter); } return newName; } 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 0902061f1c..291f8f1849 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/mitkPluginActivator.cpp @@ -1,26 +1,28 @@ /*============================================================================ 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 <mitkBoundingShapeObjectFactory.h> #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*) { }