diff --git a/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox b/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox index ac62e82201..e40b17a393 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Concepts/Properties.dox @@ -1,257 +1,236 @@ /** \page PropertiesPage Properties \tableofcontents \section PropertyConcept The Concept Behind MITK Properties Properties belong to a datanode and contain information relevant to the handling of the node by MITK. They provide a place to store additional information which is not part of the actual data, and as such have no reason to be contained within the data/file itself, but might be needed for such things as rendering (e.g. transfer functions) or interaction (e.g. the name of the node). Propteries can be read and set: \code //1: Read a property mitk::Property::Pointer readProperty = node->GetProperty("color"); //read the property "color" from the data node mitk::ColorProperty::Pointer colorProperty = dynamic_cast(readProperty); //cast this property to the subtype ColorProperty //2: Write a property mitk::BoolProperty::Pointer newProperty = mitk::BoolProperty::New( true ); //create a new property, in this case a boolean property node->SetProperty( "IsTensorVolume", newProperty ); //add this property to node with the key "IsTensorVolume" \endcode \section PropertiesPage_PropertyServices Property Services Property services enable you to extend the capabilities of the basic property system of MITK. Currently there are four property services, i.e., aliases, descriptions, extensions, and filters. All of them are briefly described in the following. More detailed information can be found in the corresponding API documentation, as all of the services are completely documented. All property services are provided through the functionalities of the \ref MicroServices_UserDocs "C++ Micro Services". Hence, you can use property services from all of your modules and plugins. \subsection PropertiesPage_PropertyAliases Property Aliases You can add an alias to a certain property name which is displayed by the Property View instead of the genuine property name. A property name can have more than one alias in which case the property is displayed multiple times in the Properties View. In addition, aliases can be easily restricted to certain data node types. Property aliases are provided through the service interface mitk::IPropertyAliases. \subsection PropertiesPage_PropertyDescriptions Property Descriptions The Property View will display a description of the currently selected property at its bottom if the property has an associated description. Descriptions are parsed and displayed as rich text. Property descriptions are provided through the service interface mitk::IPropertyDescriptions. \subsection PropertiesPage_PropertyExtensions Property Extensions Property extensions are a very generic approach to attach meta data to properties. They have the sole requirement of inheriting from mitk::PropertyExtension. The Property View searches for known property extensions like mitk::IntPropertyExtension and mitk::FloatPropertyExtension to configure the editors of the properties, e.g., you can specify the valid range for a numeric property. Property extensions are provided through the service interface mitk::IPropertyExtensions. \subsection PropertiesPage_PropertyFilters Property Filters Property filters are a rather powerful tool to filter shown properties by the Property View. There is a single global filter and the possibility to add filters on top of it for specific data node types. Filters consist of blacklist entries and whitelist entries, i.e., blacklisted entries are filtered out and whitelisted entries are the only properties which remain after filtering. If both lists contain entries only whitelisted entries are displayed that are not blacklisted. Property filters are provided through the service interface mitk::IPropertyFilters. \section ListOfIndependentProperty A List Of Module Independent Properties This section lists most of the known properties in MITK according to where they are used. Not every node needs each (or even close to) of these properties. \subsection FileManagement File Management \subsection GenericRenderingProperty Generic Rendering Properties \subsection SurfaceRenderingProperties Surface Rendering Properties \subsection VolumeRenderingProperties Volume Rendering Properties - -\remark -Uselod can be active with usegpu, usemip, useray, but any of the latter can not -be used with another one of them. - \subsection PointSetProperties Point Set Properties \subsection Geometry2DProperties Geometry2D Properties Information on properties not in this list can be found in the appropriate module. \subsection PropertiesPageSeeAlso See Also */ diff --git a/Modules/AppUtil/include/mitkBaseApplication.h b/Modules/AppUtil/include/mitkBaseApplication.h index 3d00e1b7c2..ad11d6f08a 100644 --- a/Modules/AppUtil/include/mitkBaseApplication.h +++ b/Modules/AppUtil/include/mitkBaseApplication.h @@ -1,318 +1,319 @@ /*============================================================================ 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 mitkBaseApplication_h #define mitkBaseApplication_h #include #include #include #include class ctkPluginContext; class ctkPluginFramework; class QCoreApplication; class QTranslator; namespace mitk { /** * A utility class for starting BlueBerry applications. * * In the simplest case, create an instance of this class and call run(). * This will launch a CTK plugin framework instance and execute the * default application registered by a plug-in via the * org.blueberry.osgi.applications extension point. * * This class contains many convenience methods to: * - Put the application in safe mode which catches unhandled * exceptions thrown in the Qt event loop and displays an error * message. * - Put the application in single mode which by default * sends the command line arguments to an already running instance * of the same application instead of creating a second instance. * - Add a list of library names which should be pre-loaded at * application start-up, e.g. to speed up the initial launch during * the caching process of the plug-in meta-data. * - Set a custom provisioning file to start a specific set of CTK * plug-ins during application start-up. * - Set and get CTK plugin framework properties * * The behavior can further be customized by deriving from BaseApplication * and overriding specific methods, such as: * - initializeLibraryPaths() to add specific library / plugin search paths * - defineOptions(Poco::Util::OptionSet&) to define a custom set of * command line options * - getQApplication() to provide a custom QCoreApplication instance * * A simple but complete example: * \code * #include * * int main(int argc, char* argv[]) * { * mitk::BaseApplication app(argc, argv); * app.setApplicationName("MyApp"); * app.setOrganizationName("MyOrganization"); * * // Run the workbench * return app.run(); * } * \endcode */ class MITKAPPUTIL_EXPORT BaseApplication : public Poco::Util::Application { public: // Command line arguments static const QString ARG_APPLICATION; static const QString ARG_CLEAN; static const QString ARG_CONSOLELOG; static const QString ARG_DEBUG; static const QString ARG_FORCE_PLUGIN_INSTALL; static const QString ARG_HOME; static const QString ARG_NEWINSTANCE; static const QString ARG_NO_LAZY_REGISTRY_CACHE_LOADING; static const QString ARG_NO_REGISTRY_CACHE; static const QString ARG_PLUGIN_CACHE; static const QString ARG_PLUGIN_DIRS; static const QString ARG_PRELOAD_LIBRARY; static const QString ARG_PRODUCT; static const QString ARG_PROVISIONING; static const QString ARG_REGISTRY_MULTI_LANGUAGE; static const QString ARG_SPLASH_IMAGE; static const QString ARG_STORAGE_DIR; static const QString ARG_XARGS; static const QString ARG_LOG_QT_MESSAGES; + static const QString ARG_SEGMENTATION_LABELSET_PRESET; // BlueBerry specific plugin framework properties static const QString PROP_APPLICATION; static const QString PROP_FORCE_PLUGIN_INSTALL; static const QString PROP_NEWINSTANCE; static const QString PROP_NO_LAZY_REGISTRY_CACHE_LOADING; static const QString PROP_NO_REGISTRY_CACHE; static const QString PROP_PRODUCT; static const QString PROP_REGISTRY_MULTI_LANGUAGE; BaseApplication(int argc, char **argv); ~BaseApplication() override; /** * Initialize the Qt library such that a QCoreApplication * instance is available and e.g. Qt widgets can be created. * * This is usually not called directly by the user. */ void initializeQt(); /** * Launches the BlueBerry framework and runs the default application * or the one specified in the PROP_APPLICATION framework property. * * @return The return code of the application after it was shut down. */ int run() override; void printHelp(const std::string &name, const std::string &value); /** * Set the application name. Same as QCoreApplication::setApplicationName. * @param name The application name. */ void setApplicationName(const QString &name); QString getApplicationName() const; /** * Set the organization name. Same as QCoreApplication::setOrganizationName. * @param name The organization name. */ void setOrganizationName(const QString &name); QString getOrganizationName() const; /** * Set the organization domain. Same as QCoreApplication::setOrganizationDomain. * @param name The organization domain. */ void setOrganizationDomain(const QString &name); QString getOrganizationDomain() const; /** * Put the application in single mode, which by default only allows * a single instance of the application to be created. * * Calling this method after run() has been called has no effect. * * @param singleMode */ void setSingleMode(bool singleMode); bool getSingleMode() const; /** * Put the application in safe mode, catching exceptions from the * Qt event loop. * * @param safeMode */ void setSafeMode(bool safeMode); bool getSafeMode() const; /** * Set a list of library names or absoulte file paths * which should be loaded at application start-up. The name * and file path may contain a library version appended at the * end and separated by a '$' charactger. * * For example liborg_mitk_gui_qt_common$1.0. * Platform specific suffixes are appended automatically. * * @param libraryBaseNames A list of library base names. */ void setPreloadLibraries(const QStringList &libraryBaseNames); /** * Get the list of library base names which should be pre-loaded. * * @return A list of pre-loaded libraries. */ QStringList getPreloadLibraries() const; /** * Set the path to the provisioning file. * * By default a provisioning file located in the same directory * as the executable and named \.provisioning * is loaded if it exists. To disable parsing of provisioning * files, use an empty string as the argument. Use a * null QString (\c QString::null ) to reset to the * default behaviour. * * @param filePath An absolute file path to the provisioning file. */ void setProvisioningFilePath(const QString &filePath); /** * Get the file path to the provisioning file. * @return The provisioning file path. */ QString getProvisioningFilePath() const; void setProperty(const QString &property, const QVariant &value); QVariant getProperty(const QString &property) const; void installTranslator(QTranslator*); bool isRunning(); void sendMessage(const QByteArray); protected: void initialize(Poco::Util::Application &self) override; void uninitialize() override; int getArgc() const; char **getArgv() const; /** * Get the framework storage directory for the CTK plugin * framework. This method is called in the initialize(Poco::Util::Application&) * method. It must not be called without a QCoreApplications instance. * * @return The CTK Plugin Framework storage directory. */ virtual QString getCTKFrameworkStorageDir() const; /** * Initialize the CppMicroServices library. * * The default implementation set the CppMicroServices storage * path to the current ctkPluginConstants::FRAMEWORK_STORAGE property * value. * * This method is called in the initialize(Poco::Util::Application&) * after the CTK Plugin Framework storage directory property * was set. */ virtual void initializeCppMicroServices(); /** * Get the QCoreApplication object. * * This method is called in the initialize(Poco::Util::Application&) * method and must create a QCoreApplication instance if the * global qApp variable is not initialized yet. * * @return The current QCoreApplication instance. This method * never returns null. */ virtual QCoreApplication *getQApplication() const; /** * Add plugin library search paths to the CTK Plugin Framework. * * This method is called in the nitialize(Poco::Util::Application&) * method after getQApplication() was called. */ virtual void initializeLibraryPaths(); /** * Runs the application for which the platform was started. The platform * must be running. *

* The given argument is passed to the application being run. If it is an invalid QVariant * then the command line arguments used in starting the platform, and not consumed * by the platform code, are passed to the application as a QStringList. *

* @param args the argument passed to the application. May be invalid * @return the result of running the application * @throws std::exception if anything goes wrong */ int main(const std::vector &args) override; /** * Define command line arguments * @param options */ void defineOptions(Poco::Util::OptionSet &options) override; QSharedPointer getFramework() const; ctkPluginContext *getFrameworkContext() const; /** * Get the initial properties for the CTK plugin framework. * * The returned map contains the initial framework properties for * initializing the CTK plugin framework. The value of specific * properties may change at runtime and differ from the initial * value. * * @return The initial CTK Plugin Framework properties. */ QHash getFrameworkProperties() const; /* * Initialize and display the splash screen if an image filename is given * */ void initializeSplashScreen(QCoreApplication * application) const; private: struct Impl; Impl* d; }; } #endif // MITKBASEAPPLICATION_H diff --git a/Modules/AppUtil/src/mitkBaseApplication.cpp b/Modules/AppUtil/src/mitkBaseApplication.cpp index e41a40fe79..28aa7a35b0 100644 --- a/Modules/AppUtil/src/mitkBaseApplication.cpp +++ b/Modules/AppUtil/src/mitkBaseApplication.cpp @@ -1,875 +1,880 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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::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 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_) 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(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 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(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(d->m_QApp)->setSafeMode(safeMode) : static_cast(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: * * The executable path is: * * 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; // 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 .ini file and parsing any // command line arguments this->loadConfiguration(); // 5. Add configuration data from the command line and the // optional .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; // 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(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) { 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_ShareOpenGLContexts); d->m_QApp = this->getSingleMode() ? static_cast(new QmitkSingleApplication(d->m_Argc, d->m_Argv, this->getSafeMode())) : static_cast(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 &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(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(d, &Impl::handleBooleanOption)); options.addOption(newInstanceOption); Poco::Util::Option cleanOption(ARG_CLEAN.toStdString(), "", "cleans the plugin cache"); cleanOption.callback(Poco::Util::OptionCallback(d, &Impl::handleClean)); options.addOption(cleanOption); Poco::Util::Option productOption(ARG_PRODUCT.toStdString(), "", "the id of the product to be launched"); productOption.argument("").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("").binding(PROP_APPLICATION.toStdString()); options.addOption(appOption); Poco::Util::Option provOption(ARG_PROVISIONING.toStdString(), "", "the location of a provisioning file"); provOption.argument("").binding(ARG_PROVISIONING.toStdString()); options.addOption(provOption); Poco::Util::Option storageDirOption(ARG_STORAGE_DIR.toStdString(), "", "the location for storing persistent application data"); storageDirOption.argument("").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(d, &Impl::handleBooleanOption)); options.addOption(consoleLogOption); Poco::Util::Option debugOption(ARG_DEBUG.toStdString(), "", "enable debug mode"); debugOption.argument("", 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(d, &Impl::handleBooleanOption)); options.addOption(forcePluginOption); Poco::Util::Option preloadLibsOption(ARG_PRELOAD_LIBRARY.toStdString(), "", "preload a library"); preloadLibsOption.argument("") .repeatable(true) .callback(Poco::Util::OptionCallback(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(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(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(d, &Impl::handleBooleanOption)); options.addOption(registryMultiLanguageOption); Poco::Util::Option splashScreenOption(ARG_SPLASH_IMAGE.toStdString(), "", "optional picture to use as a splash screen"); splashScreenOption.argument("").binding(ARG_SPLASH_IMAGE.toStdString()); options.addOption(splashScreenOption); Poco::Util::Option xargsOption(ARG_XARGS.toStdString(), "", "Extended argument list"); xargsOption.argument("").binding(ARG_XARGS.toStdString()); options.addOption(xargsOption); Poco::Util::Option logQtMessagesOption(ARG_LOG_QT_MESSAGES.toStdString(), "", "log Qt messages"); logQtMessagesOption.callback(Poco::Util::OptionCallback(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("").binding(ARG_SEGMENTATION_LABELSET_PRESET.toStdString()); + options.addOption(labelSetPresetOption); + Poco::Util::Application::defineOptions(options); } QSharedPointer 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 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(this->getQApplication()); if (nullptr != app) app->isRunning(); mitkThrow() << "Method not implemented."; } void BaseApplication::sendMessage(const QByteArray msg) { auto app = dynamic_cast(this->getQApplication()); if (nullptr != app) app->sendMessage(msg); mitkThrow() << "Method not implemented."; } } diff --git a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp index 980672e655..1dcf695ea1 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp +++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.cpp @@ -1,396 +1,386 @@ /*============================================================================ 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 "mitkContourModelSetGLMapper2D.h" #include "mitkColorProperty.h" #include "mitkContourModelSet.h" #include "mitkPlaneGeometry.h" #include "mitkProperties.h" +#include #include -#include "vtkPen.h" -#include "vtkContext2D.h" -#include "vtkContextDevice2D.h" -#include "vtkOpenGLContextDevice2D.h" +#include +#include #include "mitkManualPlacementAnnotationRenderer.h" #include "mitkBaseRenderer.h" #include "mitkContourModel.h" #include "mitkTextAnnotation2D.h" -mitk::ContourModelGLMapper2DBase::ContourModelGLMapper2DBase(): m_Initialized(false) +mitk::ContourModelGLMapper2DBase::ContourModelGLMapper2DBase() { m_PointNumbersAnnotation = mitk::TextAnnotation2D::New(); m_ControlPointNumbersAnnotation = mitk::TextAnnotation2D::New(); } mitk::ContourModelGLMapper2DBase::~ContourModelGLMapper2DBase() { } -void mitk::ContourModelGLMapper2DBase::Initialize(mitk::BaseRenderer *) -{ - vtkOpenGLContextDevice2D *device = nullptr; - device = vtkOpenGLContextDevice2D::New(); - if (device) - { - this->m_Context->Begin(device); - device->Delete(); - this->m_Initialized = true; - } - else - { - } -} - void mitk::ContourModelGLMapper2DBase::ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * /*actor*/) { + auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); + float rgba[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; // check for color prop and use it for rendering if it exists GetDataNode()->GetColor(rgba, renderer, "color"); // check for opacity prop and use it for rendering if it exists GetDataNode()->GetOpacity(rgba[3], renderer, "opacity"); - if (this->m_Context->GetPen() == nullptr) + if (localStorage->Context->GetPen() == nullptr) { return; } - this->m_Context->GetPen()->SetColorF((double)rgba[0], (double)rgba[1], (double)rgba[2], (double)rgba[3]); + localStorage->Context->GetPen()->SetColorF((double)rgba[0], (double)rgba[1], (double)rgba[2], (double)rgba[3]); } void mitk::ContourModelGLMapper2DBase::DrawContour(mitk::ContourModel *renderingContour, mitk::BaseRenderer *renderer) { if (std::find(m_RendererList.begin(), m_RendererList.end(), renderer) == m_RendererList.end()) { m_RendererList.push_back(renderer); } mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_PointNumbersAnnotation.GetPointer(), renderer); m_PointNumbersAnnotation->SetVisibility(false); mitk::ManualPlacementAnnotationRenderer::AddAnnotation(m_ControlPointNumbersAnnotation.GetPointer(), renderer); m_ControlPointNumbersAnnotation->SetVisibility(false); InternalDrawContour(renderingContour, renderer); } void mitk::ContourModelGLMapper2DBase::InternalDrawContour(mitk::ContourModel *renderingContour, mitk::BaseRenderer *renderer) { if (!renderingContour) return; - if (!this->m_Initialized) - { - this->Initialize(renderer); - } - vtkOpenGLContextDevice2D::SafeDownCast( - this->m_Context->GetDevice())->Begin(renderer->GetVtkRenderer()); + + auto* localStorage = m_LocalStorageHandler.GetLocalStorage(renderer); + + localStorage->Device = vtkSmartPointer::New(); + localStorage->Context = vtkSmartPointer::New(); + + localStorage->Device->Begin(renderer->GetVtkRenderer()); + localStorage->Context->Begin(localStorage->Device); mitk::DataNode *dataNode = this->GetDataNode(); renderingContour->UpdateOutputInformation(); const auto timestep = this->GetTimestep(); if (!renderingContour->IsEmptyTimeStep(timestep)) { // apply color and opacity read from the PropertyList ApplyColorAndOpacityProperties(renderer); mitk::ColorProperty::Pointer colorprop = dynamic_cast(dataNode->GetProperty("contour.color", renderer)); float opacity = 0.5; dataNode->GetFloatProperty("opacity", opacity, renderer); if (colorprop) { // set the color of the contour double red = colorprop->GetColor().GetRed(); double green = colorprop->GetColor().GetGreen(); double blue = colorprop->GetColor().GetBlue(); - this->m_Context->GetPen()->SetColorF(red, green, blue, opacity); + localStorage->Context->GetPen()->SetColorF(red, green, blue, opacity); } mitk::ColorProperty::Pointer selectedcolor = dynamic_cast(dataNode->GetProperty("contour.points.color", renderer)); if (!selectedcolor) { selectedcolor = mitk::ColorProperty::New(1.0, 0.0, 0.1); } vtkLinearTransform *transform = dataNode->GetVtkTransform(); // ContourModel::OutputType point; mitk::Point3D point; mitk::Point3D p; float vtkp[3]; float lineWidth = 3.0; bool drawit = false; bool isHovering = false; dataNode->GetBoolProperty("contour.hovering", isHovering); if (isHovering) dataNode->GetFloatProperty("contour.hovering.width", lineWidth); else dataNode->GetFloatProperty("contour.width", lineWidth); bool showSegments = false; dataNode->GetBoolProperty("contour.segments.show", showSegments); bool showControlPoints = false; dataNode->GetBoolProperty("contour.controlpoints.show", showControlPoints); bool showPoints = false; dataNode->GetBoolProperty("contour.points.show", showPoints); bool showPointsNumbers = false; dataNode->GetBoolProperty("contour.points.text", showPointsNumbers); bool showControlPointsNumbers = false; dataNode->GetBoolProperty("contour.controlpoints.text", showControlPointsNumbers); bool projectmode = false; dataNode->GetVisibility(projectmode, renderer, "contour.project-onto-plane"); auto pointsIt = renderingContour->IteratorBegin(timestep); Point2D pt2d; // projected_p in display coordinates Point2D lastPt2d; int index = 0; mitk::ScalarType maxDiff = 0.25; while (pointsIt != renderingContour->IteratorEnd(timestep)) { lastPt2d = pt2d; point = (*pointsIt)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToView(p, pt2d); ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p)); // project to plane if (projectmode) { drawit = true; } else if (scalardiff < maxDiff) // point is close enough to be drawn { drawit = true; } else { drawit = false; } // draw line if (drawit) { if (showSegments) { // lastPt2d is not valid in first step if (!(pointsIt == renderingContour->IteratorBegin(timestep))) { - this->m_Context->GetPen()->SetWidth(lineWidth); - this->m_Context->DrawLine(pt2d[0], pt2d[1], lastPt2d[0], lastPt2d[1]); - this->m_Context->GetPen()->SetWidth(1); + localStorage->Context->GetPen()->SetWidth(lineWidth); + localStorage->Context->DrawLine(pt2d[0], pt2d[1], lastPt2d[0], lastPt2d[1]); + localStorage->Context->GetPen()->SetWidth(1); } } if (showControlPoints) { // draw ontrol points if ((*pointsIt)->IsControlPoint) { float pointsize = 4; Point2D tmp; Vector2D horz, vert; horz[1] = 0; vert[0] = 0; horz[0] = pointsize; vert[1] = pointsize; - this->m_Context->GetPen()->SetColorF(selectedcolor->GetColor().GetRed(), + localStorage->Context->GetPen()->SetColorF(selectedcolor->GetColor().GetRed(), selectedcolor->GetColor().GetBlue(), selectedcolor->GetColor().GetGreen()); - this->m_Context->GetPen()->SetWidth(1); + localStorage->Context->GetPen()->SetWidth(1); // a rectangle around the point with the selected color auto* rectPts = new float[8]; tmp = pt2d - horz; rectPts[0] = tmp[0]; rectPts[1] = tmp[1]; tmp = pt2d + vert; rectPts[2] = tmp[0]; rectPts[3] = tmp[1]; tmp = pt2d + horz; rectPts[4] = tmp[0]; rectPts[5] = tmp[1]; tmp = pt2d - vert; rectPts[6] = tmp[0]; rectPts[7] = tmp[1]; - this->m_Context->DrawPolygon(rectPts,4); + localStorage->Context->DrawPolygon(rectPts,4); // the actual point in the specified color to see the usual color of the point - this->m_Context->GetPen()->SetColorF( + localStorage->Context->GetPen()->SetColorF( colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue()); - this->m_Context->DrawPoint(pt2d[0], pt2d[1]); + localStorage->Context->DrawPoint(pt2d[0], pt2d[1]); } } if (showPoints) { float pointsize = 3; Point2D tmp; Vector2D horz, vert; horz[1] = 0; vert[0] = 0; horz[0] = pointsize; vert[1] = pointsize; - this->m_Context->GetPen()->SetColorF(0.0, 0.0, 0.0); - this->m_Context->GetPen()->SetWidth(1); + localStorage->Context->GetPen()->SetColorF(0.0, 0.0, 0.0); + localStorage->Context->GetPen()->SetWidth(1); // a rectangle around the point with the selected color auto* rectPts = new float[8]; tmp = pt2d - horz; rectPts[0] = tmp[0]; rectPts[1] = tmp[1]; tmp = pt2d + vert; rectPts[2] = tmp[0]; rectPts[3] = tmp[1]; tmp = pt2d + horz; rectPts[4] = tmp[0]; rectPts[5] = tmp[1]; tmp = pt2d - vert; rectPts[6] = tmp[0]; rectPts[7] = tmp[1]; - this->m_Context->DrawPolygon(rectPts, 4); + localStorage->Context->DrawPolygon(rectPts, 4); // the actual point in the specified color to see the usual color of the point - this->m_Context->GetPen()->SetColorF( + localStorage->Context->GetPen()->SetColorF( colorprop->GetColor().GetRed(), colorprop->GetColor().GetGreen(), colorprop->GetColor().GetBlue()); - this->m_Context->DrawPoint(pt2d[0], pt2d[1]); + localStorage->Context->DrawPoint(pt2d[0], pt2d[1]); } if (showPointsNumbers) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); float rgb[3]; rgb[0] = 0.0; rgb[1] = 0.0; rgb[2] = 0.0; WriteTextWithAnnotation(m_PointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer); } if (showControlPointsNumbers && (*pointsIt)->IsControlPoint) { std::string l; std::stringstream ss; ss << index; l.append(ss.str()); float rgb[3]; rgb[0] = 1.0; rgb[1] = 1.0; rgb[2] = 0.0; WriteTextWithAnnotation(m_ControlPointNumbersAnnotation, l.c_str(), rgb, pt2d, renderer); } index++; } pointsIt++; } // end while iterate over controlpoints // close contour if necessary if (renderingContour->IsClosed(timestep) && drawit && showSegments) { lastPt2d = pt2d; point = renderingContour->GetVertexAt(0, timestep)->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToDisplay(p, pt2d); - this->m_Context->GetPen()->SetWidth(lineWidth); - this->m_Context->DrawLine(lastPt2d[0], lastPt2d[1], pt2d[0], pt2d[1]); - this->m_Context->GetPen()->SetWidth(1); + localStorage->Context->GetPen()->SetWidth(lineWidth); + localStorage->Context->DrawLine(lastPt2d[0], lastPt2d[1], pt2d[0], pt2d[1]); + localStorage->Context->GetPen()->SetWidth(1); } // draw selected vertex if exists if (renderingContour->GetSelectedVertex()) { // transform selected vertex point = renderingContour->GetSelectedVertex()->Coordinates; itk2vtk(point, vtkp); transform->TransformPoint(vtkp, vtkp); vtk2itk(vtkp, p); renderer->WorldToDisplay(p, pt2d); ScalarType scalardiff = fabs(renderer->GetCurrentWorldPlaneGeometry()->SignedDistance(p)); //---------------------------------- // draw point if close to plane if (scalardiff < maxDiff) { float pointsize = 5; Point2D tmp; - this->m_Context->GetPen()->SetColorF(0.0, 1.0, 0.0); - this->m_Context->GetPen()->SetWidth(1); + localStorage->Context->GetPen()->SetColorF(0.0, 1.0, 0.0); + localStorage->Context->GetPen()->SetWidth(1); // a rectangle around the point with the selected color auto* rectPts = new float[8]; // a diamond around the point // begin from upper left corner and paint clockwise rectPts[0] = pt2d[0] - pointsize; rectPts[1] = pt2d[1] + pointsize; rectPts[2] = pt2d[0] + pointsize; rectPts[3] = pt2d[1] + pointsize; rectPts[4] = pt2d[0] + pointsize; rectPts[5] = pt2d[1] - pointsize; rectPts[6] = pt2d[0] - pointsize; rectPts[7] = pt2d[1] - pointsize; - this->m_Context->DrawPolygon(rectPts, 4); + localStorage->Context->DrawPolygon(rectPts, 4); } //------------------------------------ } } - this->m_Context->GetDevice()->End(); + + localStorage->Context = nullptr; + localStorage->Device = nullptr; } void mitk::ContourModelGLMapper2DBase::WriteTextWithAnnotation(TextAnnotationPointerType textAnnotation, const char *text, float rgb[3], Point2D /*pt2d*/, mitk::BaseRenderer * /*renderer*/) { textAnnotation->SetText(text); textAnnotation->SetColor(rgb); textAnnotation->SetOpacity(1); textAnnotation->SetFontSize(16); textAnnotation->SetBoolProperty("drawShadow", false); textAnnotation->SetVisibility(true); } diff --git a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.h b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.h index 0ed3d77bf2..8672c23964 100644 --- a/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.h +++ b/Modules/ContourModel/Rendering/mitkContourModelGLMapper2DBase.h @@ -1,72 +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 MITK_ContourModelGLMapper2DBase_H_ #define MITK_ContourModelGLMapper2DBase_H_ #include "mitkCommon.h" #include "mitkMapper.h" #include "mitkTextAnnotation2D.h" #include -#include "vtkNew.h" +#include class vtkContext2D; +class vtkOpenGLContextDevice2D; class vtkPen; namespace mitk { class BaseRenderer; class ContourModel; /** * @brief Base class for OpenGL based 2D mappers. * Provides functionality to draw a contour. * * @ingroup MitkContourModelModule */ class MITKCONTOURMODEL_EXPORT ContourModelGLMapper2DBase : public Mapper { public: mitkClassMacro(ContourModelGLMapper2DBase, Mapper); void ApplyColorAndOpacityProperties(mitk::BaseRenderer *renderer, vtkActor * actor = nullptr) override; protected: typedef TextAnnotation2D::Pointer TextAnnotationPointerType; ContourModelGLMapper2DBase(); ~ContourModelGLMapper2DBase() override; void DrawContour(mitk::ContourModel *contour, mitk::BaseRenderer *renderer); void WriteTextWithAnnotation( TextAnnotationPointerType textAnnotation, const char *text, float rgb[3], Point2D pt2d, mitk::BaseRenderer *); virtual void InternalDrawContour(mitk::ContourModel *renderingContour, mitk::BaseRenderer *renderer); - void Initialize(mitk::BaseRenderer *renderer); - TextAnnotationPointerType m_PointNumbersAnnotation; TextAnnotationPointerType m_ControlPointNumbersAnnotation; typedef std::vector RendererListType; RendererListType m_RendererList; - bool m_Initialized; + private: + struct LocalStorage + { + vtkSmartPointer Device; + vtkSmartPointer Context; + }; - vtkNew m_Context; + LocalStorageHandler m_LocalStorageHandler; }; } // namespace mitk #endif diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 3637647f25..869340a1c3 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,328 +1,327 @@ 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/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.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/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/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/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/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSource.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/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/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkIPropertyRelations.cpp DataManagement/mitkPropertyRelations.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.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/mitkDisplayInteractor.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/mitkLog.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 Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module 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/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.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/DisplayConfig.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/DisplayConfigMITKLimited.xml Interactions/DisplayConfigBlockLMB.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/Colortables/Multilabel.h b/Modules/Core/include/Colortables/Multilabel.h index a80245c1f0..86f31d1371 100644 --- a/Modules/Core/include/Colortables/Multilabel.h +++ b/Modules/Core/include/Colortables/Multilabel.h @@ -1,25 +1,25 @@ /*============================================================================ 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 _lut_Multilabel_h_ #define _lut_Multilabel_h_ static const double Multilabel[25][3] = { - { 1.0, 1.0, 0.0 }, { 0.0, 1.0, 0.0 }, { 0.0, 0.0, 1.0 }, { 1.0, 1.0, 0.4 }, { 0.0, 0.4, 0.7 }, - { 1.0, 0.0, 1.0 }, { 1.0, 0.5, 0.0 }, { 0.0, 1.0, 0.5 }, { 0.5, 0.0, 1.0 }, { 1.0, 1.0, 0.5 }, - { 0.5, 1.0, 1.0 }, { 1.0, 0.5, 0.6 }, { 1.0, 0.3, 0.3 }, { 0.4, 0.7, 1.0 }, { 0.4, 0.5, 1.0 }, - { 0.8, 0.5, 1.0 }, { 1.0, 0.3, 1.0 }, { 1.0, 0.5, 0.6 }, { 1.0, 0.5, 0.4 }, { 0.4, 0.5, 0.4 }, - { 1.0, 0.5, 0.76 }, { 0.76, 0.4, 0.4 }, { 1.0, 0.5, 0.4 }, { 0.76, 0.3, 0.4 }, { 1.0, 0.3, 0.4 } + { 0.745, 0, 0.196 }, { 0.952, 0.764, 0 }, { 0.552, 0.713, 0 }, { 0.631, 0.792, 0.945 }, { 0.639, 0.250, 0.725 }, + { 0, 0.403, 0.647 }, { 0.952, 0.517, 0 }, { 0.666, 0.956, 0 }, { 0.109, 1, 0.807 }, { 0.823, 0.603, 0.639 }, + { 0.533, 0.176, 0.090 }, { 1, 0.803, 0.6 }, { 0.447, 0.188, 0.588 }, { 0.988, 0.109, 0.749 }, { 0, 0.325, 0 }, + { 1, 0, 0 }, { 0.047, 0.752, 0.352 }, { 0.196, 0.513, 0.996 }, { 1, 1, 0.501 }, { 0.811, 0.396, 0.254 }, + { 0.941, 0.627, 1 }, { 0.231, 0, 0.984 }, { 0.086, 1, 0.196 }, { 0.109, 0.513, 0.337 }, { 0.984, 0.894, 0.149 } }; #endif diff --git a/Modules/Core/include/mitkVtkVolumeRenderingProperty.h b/Modules/Core/include/mitkVtkVolumeRenderingProperty.h deleted file mode 100644 index 7d9c8df434..0000000000 --- a/Modules/Core/include/mitkVtkVolumeRenderingProperty.h +++ /dev/null @@ -1,104 +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. - -============================================================================*/ - -#ifndef _MITK_VTK_VOLUME_RENDERING_PROPERTY__H_ -#define _MITK_VTK_VOLUME_RENDERING_PROPERTY__H_ - -#include "mitkEnumerationProperty.h" - -#define VTK_RAY_CAST_COMPOSITE_FUNCTION 1 -#define VTK_VOLUME_RAY_CAST_MIP_FUNCTION 2 -namespace mitk -{ -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4522) -#endif - - /** - * Encapsulates the enumeration for volume rendering. Valid values are - * (VTK constant/Id/string representation): - * VTK_VOLUME_RAY_CAST_MIP_FUNCTION - * VTK_RAY_CAST_COMPOSITE_FUNCTION - * Default is nullptr - */ - class MITKCORE_EXPORT VtkVolumeRenderingProperty : public EnumerationProperty - { - public: - mitkClassMacro(VtkVolumeRenderingProperty, EnumerationProperty); - - itkFactorylessNewMacro(Self); - - itkCloneMacro(Self); - - mitkNewMacro1Param(VtkVolumeRenderingProperty, const IdType &); - - mitkNewMacro1Param(VtkVolumeRenderingProperty, const std::string &); - - /** - * Returns the current volume rendering type - */ - virtual int GetRenderingType(); - - /** - * Sets the rendering type to VTK_VOLUME_RAY_CAST_MIP_FUNCTION - */ - virtual void SetRenderingTypeToMIP(); - - /** - * Sets the rendering type to VTK_RAY_CAST_COMPOSITE_FUNCTION - */ - virtual void SetRenderingTypeToComposite(); - - using BaseProperty::operator=; - - protected: - /** Sets rendering type to default (VTK_RAY_CAST_COMPOSITE_FUNCTION). - */ - VtkVolumeRenderingProperty(); - - /** - * Constructor. Sets rendering type to the given value. - */ - VtkVolumeRenderingProperty(const IdType &value); - - /** - * Constructor. Sets rendering type to the given value. - */ - VtkVolumeRenderingProperty(const std::string &value); - - /** - * this function is overridden as protected, so that the user may not add - * additional invalid rendering types. - */ - bool AddEnum(const std::string &name, const IdType &id) override; - - /** - * Adds the enumeration types as defined by vtk to the list of known - * enumeration values. - */ - virtual void AddRenderingTypes(); - - private: - // purposely not implemented - VtkVolumeRenderingProperty &operator=(const VtkVolumeRenderingProperty &); - - itk::LightObject::Pointer InternalClone() const override; - }; - -#ifdef _MSC_VER -#pragma warning(pop) -#endif - -} // end of namespace mitk - -#endif diff --git a/Modules/Core/src/DataManagement/mitkPointSet.cpp b/Modules/Core/src/DataManagement/mitkPointSet.cpp index 024ad92dec..7a05bb3bc0 100755 --- a/Modules/Core/src/DataManagement/mitkPointSet.cpp +++ b/Modules/Core/src/DataManagement/mitkPointSet.cpp @@ -1,965 +1,965 @@ /*============================================================================ 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 "mitkPointSet.h" #include "mitkInteractionConst.h" #include "mitkPointOperation.h" #include #include namespace mitk { itkEventMacroDefinition(PointSetEvent, itk::AnyEvent); itkEventMacroDefinition(PointSetMoveEvent, PointSetEvent); itkEventMacroDefinition(PointSetSizeChangeEvent, PointSetEvent); itkEventMacroDefinition(PointSetAddEvent, PointSetSizeChangeEvent); itkEventMacroDefinition(PointSetRemoveEvent, PointSetSizeChangeEvent); itkEventMacroDefinition(PointSetExtendTimeRangeEvent, PointSetEvent); } mitk::PointSet::PointSet() : m_CalculateBoundingBox(true) { this->InitializeEmpty(); } mitk::PointSet::PointSet(const PointSet &other) : BaseData(other), m_PointSetSeries(other.GetPointSetSeriesSize()), m_CalculateBoundingBox(true) { // Copy points for (std::size_t t = 0; t < m_PointSetSeries.size(); ++t) { m_PointSetSeries[t] = DataType::New(); DataType::Pointer otherPts = other.GetPointSet(t); for (PointsConstIterator i = other.Begin(t); i != other.End(t); ++i) { m_PointSetSeries[t]->SetPoint(i.Index(), i.Value()); PointDataType pointData; if (otherPts->GetPointData(i.Index(), &pointData)) { m_PointSetSeries[t]->SetPointData(i.Index(), pointData); } } } } mitk::PointSet::~PointSet() { this->ClearData(); } void mitk::PointSet::ClearData() { m_PointSetSeries.clear(); Superclass::ClearData(); } void mitk::PointSet::InitializeEmpty() { m_PointSetSeries.resize(1); m_PointSetSeries[0] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[0]->SetPointData(pointData); m_CalculateBoundingBox = false; Superclass::InitializeTimeGeometry(1); m_Initialized = true; m_EmptyPointsContainer = DataType::PointsContainer::New(); } bool mitk::PointSet::IsEmptyTimeStep(unsigned int t) const { return IsInitialized() && (GetSize(t) == 0); } void mitk::PointSet::Expand(unsigned int timeSteps) { // Check if the vector is long enough to contain the new element // at the given position. If not, expand it with sufficient pre-initialized // elements. // // NOTE: This method will never REDUCE the vector size; it should only // be used to make sure that the vector has enough elements to include the // specified time step. unsigned int oldSize = m_PointSetSeries.size(); if (timeSteps > oldSize) { Superclass::Expand(timeSteps); m_PointSetSeries.resize(timeSteps); for (unsigned int i = oldSize; i < timeSteps; ++i) { m_PointSetSeries[i] = DataType::New(); PointDataContainer::Pointer pointData = PointDataContainer::New(); m_PointSetSeries[i]->SetPointData(pointData); } // if the size changes, then compute the bounding box m_CalculateBoundingBox = true; this->InvokeEvent(PointSetExtendTimeRangeEvent()); } } unsigned int mitk::PointSet::GetPointSetSeriesSize() const { return m_PointSetSeries.size(); } int mitk::PointSet::GetSize(unsigned int t) const { if (t < m_PointSetSeries.size()) { return m_PointSetSeries[t]->GetNumberOfPoints(); } else { return 0; } } mitk::PointSet::DataType::Pointer mitk::PointSet::GetPointSet(int t) const { if (t < (int)m_PointSetSeries.size()) { return m_PointSetSeries[t]; } else { return nullptr; } } mitk::PointSet::PointsIterator mitk::PointSet::Begin(int t) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsConstIterator mitk::PointSet::Begin(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->Begin(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsIterator mitk::PointSet::End(int t) { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsConstIterator mitk::PointSet::End(int t) const { if (t >= 0 && t < static_cast(m_PointSetSeries.size())) { return m_PointSetSeries[t]->GetPoints()->End(); } return m_EmptyPointsContainer->End(); } mitk::PointSet::PointsIterator mitk::PointSet::GetMaxId(int t) { if ((unsigned int)t >= m_PointSetSeries.size()) { return m_EmptyPointsContainer->End(); } return this->Begin(t) == this->End(t) ? this->End(t) : --End(t); } int mitk::PointSet::SearchPoint(Point3D point, ScalarType distance, int t) const { if (t >= (int)m_PointSetSeries.size()) { return -1; } // Out is the point which is checked to be the searched point PointType out; out.Fill(0); PointType indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); // Searching the first point in the Set, that is +- distance far away fro // the given point unsigned int i; PointsContainer::Iterator it, end; end = m_PointSetSeries[t]->GetPoints()->End(); int bestIndex = -1; distance = distance * distance; // To correct errors from converting index to world and world to index if (distance == 0.0) { distance = 0.000001; } ScalarType bestDist = distance; ScalarType dist, tmp; for (it = m_PointSetSeries[t]->GetPoints()->Begin(), i = 0; it != end; ++it, ++i) { bool ok = m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(it->Index(), &out); if (!ok) { return -1; } else if (indexPoint == out) // if totally equal { return it->Index(); } // distance calculation tmp = out[0] - indexPoint[0]; dist = tmp * tmp; tmp = out[1] - indexPoint[1]; dist += tmp * tmp; tmp = out[2] - indexPoint[2]; dist += tmp * tmp; if (dist < bestDist) { bestIndex = it->Index(); bestDist = dist; } } return bestIndex; } mitk::PointSet::PointType mitk::PointSet::GetPoint(PointIdentifier id, int t) const { PointType out; out.Fill(0); if ((unsigned int)t >= m_PointSetSeries.size()) { return out; } if (m_PointSetSeries[t]->GetPoints()->IndexExists(id)) { m_PointSetSeries[t]->GetPoint(id, &out); this->GetGeometry(t)->IndexToWorld(out, out); return out; } else { return out; } } bool mitk::PointSet::GetPointIfExists(PointIdentifier id, PointType *point, int t) const { if ((unsigned int)t >= m_PointSetSeries.size()) { return false; } if (m_PointSetSeries[t]->GetPoints()->GetElementIfIndexExists(id, point)) { this->GetGeometry(t)->IndexToWorld(*point, *point); return true; } else { return false; } } void mitk::PointSet::SetPoint(PointIdentifier id, PointType point, int t) { // Adapt the size of the data vector if necessary this->Expand(t + 1); mitk::Point3D indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->SetPoint(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::SetPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t) { // Adapt the size of the data vector if necessary this->Expand(t + 1); mitk::Point3D indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->SetPoint(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->SetPointData(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } void mitk::PointSet::InsertPoint(PointIdentifier id, PointType point, int t) { this->InsertPoint(id, point, mitk::PTUNDEFINED, t); } void mitk::PointSet::InsertPoint(PointIdentifier id, PointType point, PointSpecificationType spec, int t) { if ((unsigned int)t < m_PointSetSeries.size()) { mitk::Point3D indexPoint; mitk::BaseGeometry *tempGeometry = this->GetGeometry(t); if (tempGeometry == nullptr) { MITK_INFO << __FILE__ << ", l." << __LINE__ << ": GetGeometry of " << t << " returned nullptr!" << std::endl; return; } tempGeometry->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->GetPoints()->InsertElement(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = spec; m_PointSetSeries[t]->GetPointData()->InsertElement(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); } } mitk::PointSet::PointIdentifier mitk::PointSet::InsertPoint(PointType point, int t) { // Adapt the size of the data vector if necessary this->Expand(t + 1); PointIdentifier id = 0; if (m_PointSetSeries[t]->GetNumberOfPoints() > 0) { PointsIterator it = --End(t); id = it.Index(); ++id; } mitk::Point3D indexPoint; this->GetGeometry(t)->WorldToIndex(point, indexPoint); m_PointSetSeries[t]->SetPoint(id, indexPoint); PointDataType defaultPointData; defaultPointData.id = id; defaultPointData.selected = false; defaultPointData.pointSpec = mitk::PTUNDEFINED; m_PointSetSeries[t]->SetPointData(id, defaultPointData); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->Modified(); return id; } bool mitk::PointSet::RemovePointIfExists(PointIdentifier id, int t) { if ((unsigned int)t < m_PointSetSeries.size()) { DataType *pointSet = m_PointSetSeries[t]; PointsContainer *points = pointSet->GetPoints(); PointDataContainer *pdata = pointSet->GetPointData(); bool exists = points->IndexExists(id); if (exists) { points->DeleteIndex(id); pdata->DeleteIndex(id); return true; } } return false; } mitk::PointSet::PointsIterator mitk::PointSet::RemovePointAtEnd(int t) { if ((unsigned int)t < m_PointSetSeries.size()) { DataType *pointSet = m_PointSetSeries[t]; PointsContainer *points = pointSet->GetPoints(); PointDataContainer *pdata = pointSet->GetPointData(); PointsIterator bit = points->Begin(); PointsIterator eit = points->End(); if (eit != bit) { PointsContainer::ElementIdentifier id = (--eit).Index(); points->DeleteIndex(id); pdata->DeleteIndex(id); PointsIterator eit2 = points->End(); - return --eit2; + return points->empty()? eit2 : --eit2; } else { return eit; } } return m_EmptyPointsContainer->End(); } bool mitk::PointSet::SwapPointPosition(PointIdentifier id, bool moveUpwards, int t) { if (IndexExists(id, t)) { PointType point = GetPoint(id, t); if (moveUpwards) { // up if (IndexExists(id - 1, t)) { InsertPoint(id, GetPoint(id - 1, t), t); InsertPoint(id - 1, point, t); this->Modified(); return true; } } else { // down if (IndexExists(id + 1, t)) { InsertPoint(id, GetPoint(id + 1, t), t); InsertPoint(id + 1, point, t); this->Modified(); return true; } } } return false; } bool mitk::PointSet::IndexExists(int position, int t) const { if ((unsigned int)t < m_PointSetSeries.size()) { return m_PointSetSeries[t]->GetPoints()->IndexExists(position); } else { return false; } } bool mitk::PointSet::GetSelectInfo(int position, int t) const { if (this->IndexExists(position, t)) { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[t]->GetPointData(position, &pointData); return pointData.selected; } else { return false; } } void mitk::PointSet::SetSelectInfo(int position, bool selected, int t) { if (this->IndexExists(position, t)) { // timeStep to ms TimePointType timeInMS = this->GetTimeGeometry()->TimeStepToTimePoint(t); // point Point3D point = this->GetPoint(position, t); std::unique_ptr op; if (selected) { op.reset(new mitk::PointOperation(OpSELECTPOINT, timeInMS, point, position)); } else { op.reset(new mitk::PointOperation(OpDESELECTPOINT, timeInMS, point, position)); } this->ExecuteOperation(op.get()); } } mitk::PointSpecificationType mitk::PointSet::GetSpecificationTypeInfo(int position, int t) const { if (this->IndexExists(position, t)) { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[t]->GetPointData(position, &pointData); return pointData.pointSpec; } else { return PTUNDEFINED; } } int mitk::PointSet::GetNumberOfSelected(int t) const { if ((unsigned int)t >= m_PointSetSeries.size()) { return 0; } int numberOfSelected = 0; PointDataIterator it; for (it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++) { if (it->Value().selected == true) { ++numberOfSelected; } } return numberOfSelected; } int mitk::PointSet::SearchSelectedPoint(int t) const { if ((unsigned int)t >= m_PointSetSeries.size()) { return -1; } PointDataIterator it; for (it = m_PointSetSeries[t]->GetPointData()->Begin(); it != m_PointSetSeries[t]->GetPointData()->End(); it++) { if (it->Value().selected == true) { return it->Index(); } } return -1; } void mitk::PointSet::ExecuteOperation(Operation *operation) { int timeStep = -1; mitkCheckOperationTypeMacro(PointOperation, operation, pointOp); if (pointOp) { timeStep = this->GetTimeGeometry()->TimePointToTimeStep(pointOp->GetTimeInMS()); } if (timeStep < 0) { MITK_ERROR << "Time step (" << timeStep << ") outside of PointSet time bounds" << std::endl; return; } switch (operation->GetOperationType()) { case OpNOTHING: break; case OpINSERT: // inserts the point at the given position and selects it. { int position = pointOp->GetIndex(); PointType pt; pt.CastFrom(pointOp->GetPoint()); if (timeStep >= (int)this->GetTimeSteps()) this->Expand(timeStep + 1); // transfer from world to index coordinates mitk::BaseGeometry *geometry = this->GetGeometry(timeStep); if (geometry == nullptr) { MITK_INFO << "GetGeometry returned nullptr!\n"; return; } geometry->WorldToIndex(pt, pt); m_PointSetSeries[timeStep]->GetPoints()->InsertElement(position, pt); PointDataType pointData = { static_cast(pointOp->GetIndex()), pointOp->GetSelected(), pointOp->GetPointType()}; m_PointSetSeries[timeStep]->GetPointData()->InsertElement(position, pointData); this->Modified(); // boundingbox has to be computed m_CalculateBoundingBox = true; this->InvokeEvent(PointSetAddEvent()); this->OnPointSetChange(); } break; case OpMOVE: // moves the point given by index { PointType pt; pt.CastFrom(pointOp->GetPoint()); // transfer from world to index coordinates this->GetGeometry(timeStep)->WorldToIndex(pt, pt); // Copy new point into container m_PointSetSeries[timeStep]->SetPoint(pointOp->GetIndex(), pt); // Insert a default point data object to keep the containers in sync // (if no point data object exists yet) PointDataType pointData; if (!m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData)) { m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); } this->OnPointSetChange(); this->Modified(); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent(PointSetMoveEvent()); } break; case OpREMOVE: // removes the point at given by position { m_PointSetSeries[timeStep]->GetPoints()->DeleteIndex((unsigned)pointOp->GetIndex()); m_PointSetSeries[timeStep]->GetPointData()->DeleteIndex((unsigned)pointOp->GetIndex()); this->OnPointSetChange(); this->Modified(); // boundingbox has to be computed anyway m_CalculateBoundingBox = true; this->InvokeEvent(PointSetRemoveEvent()); } break; case OpSELECTPOINT: // select the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = true; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpDESELECTPOINT: // unselect the given point { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.selected = false; m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpSETPOINTTYPE: { PointDataType pointData = {0, false, PTUNDEFINED}; m_PointSetSeries[timeStep]->GetPointData(pointOp->GetIndex(), &pointData); pointData.pointSpec = pointOp->GetPointType(); m_PointSetSeries[timeStep]->SetPointData(pointOp->GetIndex(), pointData); this->Modified(); } break; case OpMOVEPOINTUP: // swap content of point with ID pointOp->GetIndex() with the point preceding it in the // container // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that precedes this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); auto it = points.find(currentID); if (it == points.end()) // ID not found break; if (it == points.begin()) // we are at the first element, there is no previous element break; /* get and cache current point & pointdata and previous point & pointdata */ --it; PointIdentifier prevID = it->first; if (this->SwapPointContents(prevID, currentID, timeStep) == true) this->Modified(); } break; case OpMOVEPOINTDOWN: // move point position within the pointset { PointIdentifier currentID = pointOp->GetIndex(); /* search for point with this id and point that succeeds this one in the data container */ PointsContainer::STLContainerType points = m_PointSetSeries[timeStep]->GetPoints()->CastToSTLContainer(); auto it = points.find(currentID); if (it == points.end()) // ID not found break; ++it; if (it == points.end()) // ID is already the last element, there is no succeeding element break; /* get and cache current point & pointdata and previous point & pointdata */ PointIdentifier nextID = it->first; if (this->SwapPointContents(nextID, currentID, timeStep) == true) this->Modified(); } break; default: itkWarningMacro("mitkPointSet could not understrand the operation. Please check!"); break; } // to tell the mappers, that the data is modified and has to be updated // only call modified if anything is done, so call in cases // this->Modified(); mitk::OperationEndEvent endevent(operation); ((const itk::Object *)this)->InvokeEvent(endevent); //*todo has to be done here, cause of update-pipeline not working yet // As discussed lately, don't mess with the rendering from inside data structures // mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void mitk::PointSet::UpdateOutputInformation() { if (this->GetSource()) { this->GetSource()->UpdateOutputInformation(); } // // first make sure, that the associated time sliced geometry has // the same number of geometry 3d's as PointSets are present // TimeGeometry *timeGeometry = GetTimeGeometry(); if (timeGeometry->CountTimeSteps() != m_PointSetSeries.size()) { itkExceptionMacro(<< "timeGeometry->CountTimeSteps() != m_PointSetSeries.size() -- use Initialize(timeSteps) with " "correct number of timeSteps!"); } // This is needed to detect zero objects mitk::ScalarType nullpoint[] = {0, 0, 0, 0, 0, 0}; BoundingBox::BoundsArrayType itkBoundsNull(nullpoint); // // Iterate over the PointSets and update the Geometry // information of each of the items. // if (m_CalculateBoundingBox) { for (unsigned int i = 0; i < m_PointSetSeries.size(); ++i) { const DataType::BoundingBoxType *bb = m_PointSetSeries[i]->GetBoundingBox(); BoundingBox::BoundsArrayType itkBounds = bb->GetBounds(); if (m_PointSetSeries[i].IsNull() || (m_PointSetSeries[i]->GetNumberOfPoints() == 0) || (itkBounds == itkBoundsNull)) { itkBounds = itkBoundsNull; continue; } // Ensure minimal bounds of 1.0 in each dimension for (unsigned int j = 0; j < 3; ++j) { if (itkBounds[j * 2 + 1] - itkBounds[j * 2] < 1.0) { BoundingBox::CoordRepType center = (itkBounds[j * 2] + itkBounds[j * 2 + 1]) / 2.0; itkBounds[j * 2] = center - 0.5; itkBounds[j * 2 + 1] = center + 0.5; } } this->GetGeometry(i)->SetBounds(itkBounds); } m_CalculateBoundingBox = false; } this->GetTimeGeometry()->Update(); } void mitk::PointSet::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PointSet::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PointSet::VerifyRequestedRegion() { return true; } void mitk::PointSet::SetRequestedRegion(const DataObject *) { } void mitk::PointSet::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << "Number timesteps: " << m_PointSetSeries.size() << "\n"; unsigned int i = 0; for (auto it = m_PointSetSeries.begin(); it != m_PointSetSeries.end(); ++it) { os << indent << "Timestep " << i++ << ": \n"; MeshType::Pointer ps = *it; itk::Indent nextIndent = indent.GetNextIndent(); ps->Print(os, nextIndent); MeshType::PointsContainer *points = ps->GetPoints(); MeshType::PointDataContainer *datas = ps->GetPointData(); MeshType::PointDataContainer::Iterator dataIterator = datas->Begin(); for (MeshType::PointsContainer::Iterator pointIterator = points->Begin(); pointIterator != points->End(); ++pointIterator, ++dataIterator) { os << nextIndent << "Point " << pointIterator->Index() << ": ["; os << pointIterator->Value().GetElement(0); for (unsigned int i = 1; i < PointType::GetPointDimension(); ++i) { os << ", " << pointIterator->Value().GetElement(i); } os << "]"; os << ", selected: " << dataIterator->Value().selected << ", point spec: " << dataIterator->Value().pointSpec << "\n"; } } } bool mitk::PointSet::SwapPointContents(PointIdentifier id1, PointIdentifier id2, int timeStep) { /* search and cache contents */ PointType p1; if (m_PointSetSeries[timeStep]->GetPoint(id1, &p1) == false) return false; PointDataType data1; if (m_PointSetSeries[timeStep]->GetPointData(id1, &data1) == false) return false; PointType p2; if (m_PointSetSeries[timeStep]->GetPoint(id2, &p2) == false) return false; PointDataType data2; if (m_PointSetSeries[timeStep]->GetPointData(id2, &data2) == false) return false; /* now swap contents */ m_PointSetSeries[timeStep]->SetPoint(id1, p2); m_PointSetSeries[timeStep]->SetPointData(id1, data2); m_PointSetSeries[timeStep]->SetPoint(id2, p1); m_PointSetSeries[timeStep]->SetPointData(id2, data1); return true; } bool mitk::PointSet::PointDataType::operator==(const mitk::PointSet::PointDataType &other) const { return id == other.id && selected == other.selected && pointSpec == other.pointSpec; } bool mitk::Equal(const mitk::PointSet *leftHandSide, const mitk::PointSet *rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal( const mitk::PointSet* leftHandSide, const mitk::PointSet* rightHandSide, " "mitk::ScalarType eps, bool verbose ) does not work with nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose, checkGeometry); } bool mitk::Equal(const mitk::PointSet &leftHandSide, const mitk::PointSet &rightHandSide, mitk::ScalarType eps, bool verbose, bool checkGeometry) { bool result = true; // If comparing point sets from file, you must not compare the geometries, as they are not saved. In other cases, you // do need to check them. if (checkGeometry) { if (!mitk::Equal(*leftHandSide.GetGeometry(), *rightHandSide.GetGeometry(), eps, verbose)) { if (verbose) MITK_INFO << "[( PointSet )] Geometries differ."; result = false; } } if (leftHandSide.GetSize() != rightHandSide.GetSize()) { if (verbose) MITK_INFO << "[( PointSet )] Number of points differ."; result = false; } else { // if the size is equal, we compare the point values mitk::Point3D pointLeftHandSide; mitk::Point3D pointRightHandSide; int numberOfIncorrectPoints = 0; // Iterate over both pointsets in order to compare all points pair-wise mitk::PointSet::PointsConstIterator end = leftHandSide.End(); for (mitk::PointSet::PointsConstIterator pointSetIteratorLeft = leftHandSide.Begin(), pointSetIteratorRight = rightHandSide.Begin(); pointSetIteratorLeft != end; ++pointSetIteratorLeft, ++pointSetIteratorRight) // iterate simultaneously over both sets { pointLeftHandSide = pointSetIteratorLeft.Value(); pointRightHandSide = pointSetIteratorRight.Value(); if (!mitk::Equal(pointLeftHandSide, pointRightHandSide, eps, verbose)) { if (verbose) MITK_INFO << "[( PointSet )] Point values are different."; result = false; numberOfIncorrectPoints++; } } if ((numberOfIncorrectPoints > 0) && verbose) { MITK_INFO << numberOfIncorrectPoints << " of a total of " << leftHandSide.GetSize() << " points are different."; } } return result; } diff --git a/Modules/Core/src/DataManagement/mitkVtkVolumeRenderingProperty.cpp b/Modules/Core/src/DataManagement/mitkVtkVolumeRenderingProperty.cpp deleted file mode 100644 index 6d69eb3849..0000000000 --- a/Modules/Core/src/DataManagement/mitkVtkVolumeRenderingProperty.cpp +++ /dev/null @@ -1,75 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkVtkVolumeRenderingProperty.h" -#include - -mitk::VtkVolumeRenderingProperty::VtkVolumeRenderingProperty() -{ - this->AddRenderingTypes(); - this->SetValue(static_cast(VTK_RAY_CAST_COMPOSITE_FUNCTION)); -} - -mitk::VtkVolumeRenderingProperty::VtkVolumeRenderingProperty(const IdType &value) -{ - this->AddRenderingTypes(); - if (IsValidEnumerationValue(value)) - { - this->SetValue(value); - } - else - MITK_INFO << "Warning: invalid rendering configuration" << std::endl; -} - -mitk::VtkVolumeRenderingProperty::VtkVolumeRenderingProperty(const std::string &value) -{ - this->AddRenderingTypes(); - if (IsValidEnumerationValue(value)) - { - this->SetValue(value); - } - else - MITK_INFO << "Warning: invalid rendering configuration" << std::endl; -} - -int mitk::VtkVolumeRenderingProperty::GetRenderingType() -{ - return static_cast(this->GetValueAsId()); -} - -void mitk::VtkVolumeRenderingProperty::SetRenderingTypeToMIP() -{ - this->SetValue(static_cast(VTK_VOLUME_RAY_CAST_MIP_FUNCTION)); -} - -void mitk::VtkVolumeRenderingProperty::SetRenderingTypeToComposite() -{ - this->SetValue(static_cast(VTK_RAY_CAST_COMPOSITE_FUNCTION)); -} - -void mitk::VtkVolumeRenderingProperty::AddRenderingTypes() -{ - AddEnum("MIP", static_cast(VTK_VOLUME_RAY_CAST_MIP_FUNCTION)); - AddEnum("COMPOSITE", static_cast(VTK_RAY_CAST_COMPOSITE_FUNCTION)); -} - -bool mitk::VtkVolumeRenderingProperty::AddEnum(const std::string &name, const IdType &id) -{ - return Superclass::AddEnum(name, id); -} - -itk::LightObject::Pointer mitk::VtkVolumeRenderingProperty::InternalClone() const -{ - itk::LightObject::Pointer result(new Self(*this)); - result->UnRegister(); - return result; -} diff --git a/Modules/Core/test/mitkPointSetTest.cpp b/Modules/Core/test/mitkPointSetTest.cpp index 661ef934f9..ac84bb6389 100644 --- a/Modules/Core/test/mitkPointSetTest.cpp +++ b/Modules/Core/test/mitkPointSetTest.cpp @@ -1,483 +1,488 @@ /*============================================================================ 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 "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include #include #include #include /** * TestSuite for PointSet stuff not only operating on an empty PointSet */ class mitkPointSetTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPointSetTestSuite); MITK_TEST(TestIsNotEmpty); MITK_TEST(TestSetSelectInfo); MITK_TEST(TestGetNumberOfSelected); MITK_TEST(TestSearchSelectedPoint); MITK_TEST(TestGetPointIfExists); MITK_TEST(TestSwapPointPositionUpwards); MITK_TEST(TestSwapPointPositionUpwardsNotPossible); MITK_TEST(TestSwapPointPositionDownwards); MITK_TEST(TestSwapPointPositionDownwardsNotPossible); MITK_TEST(TestCreateHoleInThePointIDs); MITK_TEST(TestInsertPointWithPointSpecification); MITK_TEST(TestRemovePointInterface); MITK_TEST(TestMaxIdAccess); MITK_TEST(TestInsertPointAtEnd); CPPUNIT_TEST_SUITE_END(); private: mitk::PointSet::Pointer pointSet; static const mitk::PointSet::PointIdentifier selectedPointId = 2; public: void setUp() override { // Create PointSet pointSet = mitk::PointSet::New(); // add some points mitk::Point3D point2, point3, point4; point2.Fill(3); point3.Fill(4); point4.Fill(5); pointSet->InsertPoint(2, point2); pointSet->InsertPoint(3, point3); pointSet->InsertPoint(4, point4); mitk::Point3D point1; mitk::FillVector3D(point1, 1.0, 2.0, 3.0); pointSet->InsertPoint(1, point1); mitk::Point3D point0; point0.Fill(1); pointSet->InsertPoint(0, point0); // select point with id 2 pointSet->SetSelectInfo(2, true); } void tearDown() override { pointSet = nullptr; } void TestIsNotEmpty() { // PointSet can not be empty! CPPUNIT_ASSERT_EQUAL_MESSAGE("check if the PointSet is not empty ", true, !pointSet->IsEmptyTimeStep(0)); /* std::cout << "check if the PointSet is not empty "; if (pointSet->IsEmpty(0)) { std::cout<<"[FAILED]"<SetSelectInfo(4, true); CPPUNIT_ASSERT_EQUAL_MESSAGE("check SetSelectInfo", true, pointSet->GetSelectInfo(4)); /* if (!pointSet->GetSelectInfo(2)) { std::cout<<"[FAILED]"<SearchSelectedPoint() == (int)selectedPointId); /* if( pointSet->SearchSelectedPoint() != 4) { std::cout<<"[FAILED]"<GetNumberOfSelected() == 1); /* if(pointSet->GetNumberOfSelected() != 1) { std::cout<<"[FAILED]"<GetPointIfExists(4, &tmpPoint); CPPUNIT_ASSERT_EQUAL_MESSAGE("check GetPointIfExists: ", true, tmpPoint == point4); /* if (tmpPoint != point5) { std::cout<<"[FAILED]"<GetPoint(1); pointSet->SwapPointPosition(1, true); tempPoint = pointSet->GetPoint(0); CPPUNIT_ASSERT_EQUAL_MESSAGE("check SwapPointPosition upwards", true, point == tempPoint); /* if(point != tempPoint) { std::cout<<"[FAILED]"<SwapPointPosition(0, true)); /* if(pointSet->SwapPointPosition(0, true)) { std::cout<<"[FAILED]"<GetPoint(0); pointSet->SwapPointPosition(0, false); tempPoint = pointSet->GetPoint(1); CPPUNIT_ASSERT_EQUAL_MESSAGE("check SwapPointPosition down", true, point == tempPoint); /* if(point != tempPoint) { std::cout<<"[FAILED]"<SetPoint(id, point); // Check SwapPointPosition downwards not possible CPPUNIT_ASSERT_EQUAL_MESSAGE( "check SwapPointPosition downwards not possible", false, pointSet2->SwapPointPosition(id, false)); /* if(pointSet->SwapPointPosition(1, false)) { std::cout<<"[FAILED]"<InsertPoint(10, p10); pointSet->InsertPoint(11, p11); pointSet->InsertPoint(12, p12); CPPUNIT_ASSERT_EQUAL_MESSAGE("add points with id 10, 11, 12: ", true, (pointSet->IndexExists(10) == true) || (pointSet->IndexExists(11) == true) || (pointSet->IndexExists(12) == true)); // check OpREMOVE ExecuteOperation int id = 11; auto doOp = new mitk::PointOperation(mitk::OpREMOVE, point, id); pointSet->ExecuteOperation(doOp); CPPUNIT_ASSERT_EQUAL_MESSAGE("remove point id 11: ", false, pointSet->IndexExists(id)); /* if(pointSet->IndexExists(id)) { std::cout<<"[FAILED]"<ExecuteOperation(doOp); delete doOp; // check OpMOVEPOINTUP ExecuteOperation doOp = new mitk::PointOperation(mitk::OpMOVEPOINTUP, p12, 12); pointSet->ExecuteOperation(doOp); delete doOp; mitk::PointSet::PointType newP10 = pointSet->GetPoint(10); mitk::PointSet::PointType newP12 = pointSet->GetPoint(12); CPPUNIT_ASSERT_EQUAL_MESSAGE( "check PointOperation OpMOVEPOINTUP for point id 12:", true, ((newP10 == p12) && (newP12 == p10))); // check OpMOVEPOINTDOWN ExecuteOperation doOp = new mitk::PointOperation(mitk::OpMOVEPOINTDOWN, p10, 10); pointSet->ExecuteOperation(doOp); delete doOp; newP10 = pointSet->GetPoint(10); newP12 = pointSet->GetPoint(12); CPPUNIT_ASSERT_EQUAL_MESSAGE( "check PointOperation OpMOVEPOINTDOWN for point id 10: ", true, ((newP10 == p10) && (newP12 == p12))); } void TestInsertPointWithPointSpecification() { // check InsertPoint with PointSpecification mitk::Point3D point5; mitk::Point3D tempPoint; point5.Fill(7); pointSet->SetPoint(5, point5, mitk::PTEDGE); tempPoint = pointSet->GetPoint(5); CPPUNIT_ASSERT_EQUAL_MESSAGE("check InsertPoint with PointSpecification", true, tempPoint == point5); /* if (tempPoint != point5) { std::cout<<"[FAILED]"<Clone(); mitk::PointSet::Pointer refPsLastRemoved = mitk::PointSet::New(); mitk::Point3D point0, point1, point2, point3, point4; point0.Fill(1); refPsLastRemoved->InsertPoint(0, point0); mitk::FillVector3D(point1, 1.0, 2.0, 3.0); refPsLastRemoved->InsertPoint(1, point1); point2.Fill(3); point3.Fill(4); refPsLastRemoved->InsertPoint(2, point2); refPsLastRemoved->InsertPoint(3, point3); mitk::PointSet::Pointer refPsMiddleRemoved = mitk::PointSet::New(); refPsMiddleRemoved->InsertPoint(0, point0); refPsMiddleRemoved->InsertPoint(1, point1); refPsMiddleRemoved->InsertPoint(3, point3); // remove non-existent point bool removed = pointSet->RemovePointIfExists(5, 0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove non-existent point", false, removed); MITK_ASSERT_EQUAL(pointSet, psClone, "No changes made"); // remove point from non-existent time-step removed = pointSet->RemovePointIfExists(1, 1); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove non-existent point", false, removed); MITK_ASSERT_EQUAL(pointSet, psClone, "No changes made"); // remove max id from non-existent time-step mitk::PointSet::PointsIterator maxIt = pointSet->RemovePointAtEnd(2); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove max id point from non-existent time step", true, maxIt == pointSet->End(2)); MITK_ASSERT_EQUAL(pointSet, psClone, "No changes made"); // remove max id from empty point set mitk::PointSet::Pointer emptyPS = mitk::PointSet::New(); maxIt = emptyPS->RemovePointAtEnd(0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove max id point from non-existent time step", true, maxIt == emptyPS->End(0)); - int size = emptyPS->GetSize(0); - unsigned int pointSetSeriesSize = emptyPS->GetPointSetSeriesSize(); - CPPUNIT_ASSERT_EQUAL_MESSAGE("Nothing added", true, size == 0 && pointSetSeriesSize == 1); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Nothing removed", true, emptyPS->GetSize(0) == 0 && emptyPS->GetPointSetSeriesSize() == 1); + + // remove max id from a point set with one point + mitk::PointSet::Pointer onePS = mitk::PointSet::New(); + onePS->InsertPoint(0, point0); + maxIt = onePS->RemovePointAtEnd(0); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Removal of last point let to invalid state of point set", true, maxIt == onePS->End(0)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Nothing removed", true, onePS->GetSize(0) == 0 && onePS->GetPointSetSeriesSize() == 1); // remove max id point maxIt = pointSet->RemovePointAtEnd(0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Point id 4 removed", false, pointSet->IndexExists(4)); MITK_ASSERT_EQUAL(pointSet, refPsLastRemoved, "No changes made"); mitk::PointSet::PointIdentifier id = maxIt.Index(); mitk::PointSet::PointType refPt; refPt[0] = 4.0; refPt[1] = 4.0; refPt[2] = 4.0; mitk::PointSet::PointType pt = maxIt.Value(); bool equal = mitk::Equal(refPt, pt); CPPUNIT_ASSERT_EQUAL_MESSAGE("Returned iterator pointing at max id", true, id == 3 && equal); // remove middle point removed = pointSet->RemovePointIfExists(2, 0); CPPUNIT_ASSERT_EQUAL_MESSAGE("Remove point id 2", true, removed); MITK_ASSERT_EQUAL(pointSet, refPsMiddleRemoved, "Point removed"); } void TestMaxIdAccess() { typedef mitk::PointSet::PointIdentifier IdType; typedef mitk::PointSet::PointsIterator PointsIteratorType; PointsIteratorType empty; mitk::Point3D new1, new2, new3, new4, refMaxPt; new1.Fill(4); new2.Fill(5); new3.Fill(6); new4.Fill(7); refMaxPt.Fill(5); pointSet->SetPoint(0, new1, 2); pointSet->InsertPoint(1, new2, 2); pointSet->InsertPoint(3, new3, 2); pointSet->InsertPoint(6, new4, 2); PointsIteratorType maxIt = pointSet->GetMaxId(1); empty = pointSet->End(1); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check empty time step max id.", true, maxIt == empty); maxIt = pointSet->GetMaxId(3); empty = pointSet->End(3); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check non-existent time step max id.", true, maxIt == empty); maxIt = pointSet->GetMaxId(0); empty = pointSet->End(0); IdType maxId = maxIt.Index(); mitk::Point3D maxPt = maxIt.Value(); bool equal = mitk::Equal(maxPt, refMaxPt); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 0 max id iterator.", false, maxIt == empty); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 0 max id.", true, maxId == 4); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 0 max id point.", true, equal); maxIt = pointSet->GetMaxId(2); empty = pointSet->End(2); maxId = maxIt.Index(); maxPt = maxIt.Value(); equal = mitk::Equal(maxPt, new4); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 2 max id iterator.", false, maxIt == empty); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 2 max id.", true, maxId == 6); CPPUNIT_ASSERT_EQUAL_MESSAGE("Check time step 2 max id point.", true, equal); } void TestInsertPointAtEnd() { typedef mitk::PointSet::PointType PointType; PointType new1, new2, new3, new4, refMaxPt; new1.Fill(4); new2.Fill(5); new3.Fill(6); new4.Fill(7); pointSet->SetPoint(1, new1, 2); pointSet->InsertPoint(3, new2, 2); pointSet->InsertPoint(4, new3, 2); pointSet->InsertPoint(6, new4, 2); PointType in1, in2, in3, in4; in1.Fill(8); in2.Fill(9); in3.Fill(10); in4.Fill(11); mitk::PointSet::Pointer refPs1 = pointSet->Clone(); refPs1->SetPoint(5, in1, 0); mitk::PointSet::Pointer refPs2 = pointSet->Clone(); refPs2->SetPoint(5, in1, 0); refPs2->SetPoint(0, in2, 1); mitk::PointSet::Pointer refPs3 = pointSet->Clone(); refPs3->SetPoint(5, in1, 0); refPs3->SetPoint(0, in2, 1); refPs3->SetPoint(7, in3, 2); mitk::PointSet::Pointer refPs4 = pointSet->Clone(); refPs4->SetPoint(5, in1, 0); refPs4->SetPoint(0, in2, 1); refPs4->SetPoint(7, in3, 2); refPs4->SetPoint(0, in4, 7); pointSet->InsertPoint(in1, 0); MITK_ASSERT_EQUAL(pointSet, refPs1, "Check point insertion for time step 0."); pointSet->InsertPoint(in2, 1); MITK_ASSERT_EQUAL(pointSet, refPs2, "Check point insertion for time step 1."); pointSet->InsertPoint(in3, 2); MITK_ASSERT_EQUAL(pointSet, refPs3, "Check point insertion for time step 2."); pointSet->InsertPoint(in4, 7); MITK_ASSERT_EQUAL(pointSet, refPs4, "Check point insertion for time step 7."); } }; MITK_TEST_SUITE_REGISTRATION(mitkPointSet) diff --git a/Modules/IGT/TrackingDevices/mitkClaronInterface.h b/Modules/IGT/TrackingDevices/mitkClaronInterface.h index 40e8531e77..1840c8ad83 100644 --- a/Modules/IGT/TrackingDevices/mitkClaronInterface.h +++ b/Modules/IGT/TrackingDevices/mitkClaronInterface.h @@ -1,145 +1,145 @@ /*============================================================================ 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 MITKCLARONINTERFACE_H_HEADER_INCLUDED_ #define MITKCLARONINTERFACE_H_HEADER_INCLUDED_ #define MTC(func) {int r = func; if (r!=mtOK) printf("MTC error: %s\n",MTLastErrorString()); }; #include #include #include #include "mitkCommon.h" #include #include #ifdef _WIN64 //Defined for applications for Win64. -typedef long mtHandle; +typedef long long mtHandle; #else typedef int mtHandle; #endif namespace mitk { typedef int claronToolHandle; /** Documentation: * \brief An object of this class represents the interface to the MicronTracker. The methods of this class * are calling the c-functions which are provided by the MTC-library. If the MicronTracker is not in * use, which means the CMake-variable "MITK_USE_MICRON_TRACKER" is set to OFF, this class is replaced * by a stub class called "ClaronInterfaceStub". * \ingroup IGT */ class MITKIGT_EXPORT ClaronInterface : public itk::Object { public: mitkClassMacroItkParent(ClaronInterface,itk::Object); itkFactorylessNewMacro(Self); itkCloneMacro(Self) /** * \brief Initialization of claroninterface. * \param calibrationDir The directory where the device can find the camera calibration file. * \param toolFilesDir The directory for the tool files. */ void Initialize(std::string calibrationDir, std::string toolFilesDir); /** * \brief Opens the connection to the device and makes it ready to track tools. * \return Returns true if there is a connection to the device and the device is ready to track tools, false if not. */ bool StartTracking(); /** * \brief Clears all resources. After this method have been called the system isn't ready to track any longer. * \return Returns true if the operation was succesful, false if not. */ bool StopTracking(); /** * \return Returns all tools which have been detected at the last frame grab. */ std::vector GetAllActiveTools(); /** * \return Returns the position of the tooltip. If no tooltip is defined the Method returns the position of the tool. */ std::vector GetTipPosition(claronToolHandle c); /** * \return Returns the quarternions of the tooltip. If no tooltip is defined the Method returns the quarternions of the tool. */ std::vector GetTipQuaternions(claronToolHandle c); /** * \return Returns the position of the tool */ std::vector GetPosition(claronToolHandle c); /** * \return Returns the quaternion of the tool. */ std::vector GetQuaternions(claronToolHandle c); /** * \return Returns the name of the tool. This name is given by the calibration file. * \param c The handle of the tool, which name should be given back. */ const char* GetName(claronToolHandle c); /** * \brief Grabs a frame from the camera. */ void GrabFrame(); /** * \return Returns wether the tracking device is tracking or not. */ bool IsTracking(); /** * \return Returns wether the MicronTracker is installed (means wether the C-Make-Variable "MITK_USE_MICRON_TRACKER" is set ON), * so returns true in this case. This is because the class mitkClaronInterfaceStub, in which the same Method returns false * is used otherways. */ bool IsMicronTrackerInstalled(); protected: /** * \brief standard constructor */ ClaronInterface(); /** * \brief standard destructor */ ~ClaronInterface() override; /** \brief Variable is true if the device is tracking at the moment, false if not.*/ bool isTracking; /** \brief Variable which holds the directory which should contain the file BumbleBee_6400420.calib. This directory is needed by the MTC library.*/ char calibrationDir[512]; /** \brief Variable which holds a directory with some tool files in it. All this tools are trackable when the path is given to the MTC library.*/ char markerDir[512]; //Some handles to communicate with the MTC library. mtHandle IdentifiedMarkers; mtHandle PoseXf; mtHandle CurrCamera; mtHandle IdentifyingCamera; //------------------------------------------------ }; }//mitk #endif diff --git a/Modules/IGT/TrackingDevices/mitkPolhemusTool.h b/Modules/IGT/TrackingDevices/mitkPolhemusTool.h index b16589a3b5..9fa679e67c 100644 --- a/Modules/IGT/TrackingDevices/mitkPolhemusTool.h +++ b/Modules/IGT/TrackingDevices/mitkPolhemusTool.h @@ -1,84 +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 MITKPolhemusTOOL_H_HEADER_INCLUDED_ #define MITKPolhemusTOOL_H_HEADER_INCLUDED_ #include #include -#include namespace mitk { class PolhemusTrackingDevice; /** Documentation: * \brief An object of this class represents a tool of a Polhemus tracking device. * A tool has to be added to a tracking device which will then * continuously update the tool coordinates. * \ingroup IGT */ class MITKIGT_EXPORT PolhemusTool : public TrackingTool { public: friend class PolhemusTrackingDevice; mitkClassMacro(PolhemusTool, TrackingTool); enum DistortionLevel { UNDEFINED, ///< Distortion level is not determined. NO_DISTORTION, ///< System operational with a good quality magnetic signal. MINOR_DISTORTION, ///< System operational with a marginal magnetic signal. SIGNIFICANT_DISTORTION ///< System operational with a poor magnetic signal. }; /** * \brief Sets the port of the tool. (e.g. 1 for port "SENS 1" etc.) */ virtual void SetToolPort(int _ToolPort); /** * \brief Sets the port of the tool. (e.g. 1 for port "SENS 1" etc.) */ virtual int GetToolPort(); /** * \brief Sets the distortion level by mapping the integer value (read from the response frame) to the corresponding enumeration literal. * * According to the Polhemus Liberty documentation: * - 0 means system operational with a good quality magnetic signal. No distortion. * - 1 means system operational with a marginal magnetic signal. Minor distortion. * - 2 means system operational with a poor magnetic signal. Significant distortion. * * \param level The distortion level represented as 0, 1 or 2. */ void SetDistortionLevel(const int level); /** * \brief Returns the distortion level. * \return The distortion level. UNDEFINED, if distortion level is not determined. */ DistortionLevel GetDistortionLevel() const; protected: itkFactorylessNewMacro(Self); itkCloneMacro(Self) PolhemusTool(); virtual ~PolhemusTool(); //This is the port, on which the tool is connected. It is identical with the "ToolIdentifier" set on NavigationDataTools. //If tool is connected on port "SENS 2", the m_ToolPort is 2 etc. int m_ToolPort; DistortionLevel m_DistortionLevel; }; }//mitk #endif // MITKPolhemusTOOL_H_HEADER_INCLUDED_ diff --git a/Modules/IGT/TrackingDevices/mitkPolhemusTrackerTypeInformation.cpp b/Modules/IGT/TrackingDevices/mitkPolhemusTrackerTypeInformation.cpp index 1d8a9374b1..94dc4bc874 100644 --- a/Modules/IGT/TrackingDevices/mitkPolhemusTrackerTypeInformation.cpp +++ b/Modules/IGT/TrackingDevices/mitkPolhemusTrackerTypeInformation.cpp @@ -1,66 +1,66 @@ /*============================================================================ 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 "mitkPolhemusTrackerTypeInformation.h" #include "mitkPolhemusTrackingDevice.h" namespace mitk { std::string PolhemusTrackerTypeInformation::GetTrackingDeviceName() { return "Polhemus tracker"; } TrackingDeviceData PolhemusTrackerTypeInformation::GetDeviceDataPolhemusTrackerLiberty() { TrackingDeviceData data = { PolhemusTrackerTypeInformation::GetTrackingDeviceName(), "Polhemus Liberty Tracker", "cube", "X" }; return data; } PolhemusTrackerTypeInformation::PolhemusTrackerTypeInformation() { m_DeviceName = PolhemusTrackerTypeInformation::GetTrackingDeviceName(); m_TrackingDeviceData.push_back(GetDeviceDataPolhemusTrackerLiberty()); } PolhemusTrackerTypeInformation::~PolhemusTrackerTypeInformation() { } mitk::TrackingDeviceSource::Pointer PolhemusTrackerTypeInformation::CreateTrackingDeviceSource( mitk::TrackingDevice::Pointer trackingDevice, mitk::NavigationToolStorage::Pointer navigationTools, std::string* errorMessage, std::vector* toolCorrespondencesInToolStorage) { mitk::TrackingDeviceSource::Pointer returnValue = mitk::TrackingDeviceSource::New(); mitk::PolhemusTrackingDevice::Pointer thisDevice = dynamic_cast(trackingDevice.GetPointer()); *toolCorrespondencesInToolStorage = std::vector(); //add the tools to the tracking device for (unsigned int i = 0; i < navigationTools->GetToolCount(); i++) { mitk::NavigationTool::Pointer thisNavigationTool = navigationTools->GetTool(i); toolCorrespondencesInToolStorage->push_back(i); bool toolAddSuccess = thisDevice->AddTool(thisNavigationTool->GetToolName().c_str(), std::stoi(thisNavigationTool->GetIdentifier())); if (!toolAddSuccess) { //todo error handling errorMessage->append("Can't add tool, is the toolfile valid?"); - return NULL; + return nullptr; } thisDevice->GetTool(i)->SetToolTipPosition(thisNavigationTool->GetToolTipPosition(), thisNavigationTool->GetToolAxisOrientation()); } returnValue->SetTrackingDevice(thisDevice); return returnValue; } } diff --git a/Modules/IGT/TrackingDevices/mitkPolhemusTrackingDevice.cpp b/Modules/IGT/TrackingDevices/mitkPolhemusTrackingDevice.cpp index 628637fc3d..156213927c 100644 --- a/Modules/IGT/TrackingDevices/mitkPolhemusTrackingDevice.cpp +++ b/Modules/IGT/TrackingDevices/mitkPolhemusTrackingDevice.cpp @@ -1,325 +1,304 @@ /*============================================================================ 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 "mitkPolhemusTrackingDevice.h" #include "mitkPolhemusTool.h" #include "mitkIGTConfig.h" #include "mitkIGTTimeStamp.h" #include "mitkIGTHardwareException.h" #include #include -#include +#include #include "mitkPolhemusTrackerTypeInformation.h" -typedef itk::MutexLockHolder MutexLockHolder; - mitk::PolhemusTrackingDevice::PolhemusTrackingDevice() : mitk::TrackingDevice() { //set the type of this tracking device this->m_Data = mitk::PolhemusTrackerTypeInformation::GetDeviceDataPolhemusTrackerLiberty(); - - this->m_MultiThreader = itk::MultiThreader::New(); - m_ThreadID = 0; - m_Device = mitk::PolhemusInterface::New(); } mitk::PolhemusTrackingDevice::~PolhemusTrackingDevice() { } bool mitk::PolhemusTrackingDevice::IsDeviceInstalled() { return true; } mitk::TrackingTool* mitk::PolhemusTrackingDevice::AddTool(const char* toolName, int toolPort) { //Only add tool if port isn't already used. for (auto _tool : m_AllTools) { if (_tool->GetToolPort() == toolPort) { MITK_DEBUG << "There is already a tool connected to this port. Returning existing tool"; return _tool; } } mitk::PolhemusTool::Pointer t = mitk::PolhemusTool::New(); t->SetToolName(toolName); t->SetToolPort(toolPort); if (this->InternalAddTool(t) == false) return nullptr; return t.GetPointer(); } bool mitk::PolhemusTrackingDevice::InternalAddTool(PolhemusTool::Pointer tool) { m_AllTools.push_back(tool); return true; } bool mitk::PolhemusTrackingDevice::StartTracking() { bool success = m_Device->StartTracking(); if (success) { mitk::IGTTimeStamp::GetInstance()->Start(this); this->SetState(Tracking); - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); this->m_StopTracking = false; - this->m_StopTrackingMutex->Unlock(); - m_ThreadID = m_MultiThreader->SpawnThread(this->ThreadStartTracking, this); // start a new thread that executes the TrackTools() method - return true; + this->m_StopTrackingMutex.unlock(); + if (m_Thread.joinable()) {m_Thread.detach();} + m_Thread = std::thread(&PolhemusTrackingDevice::ThreadStartTracking, this); // start a new thread that executes the TrackTools() methodreturn true; } else { this->SetState(Ready); mitkThrowException(mitk::IGTHardwareException) << "Error while trying to start the device!"; } return success; } bool mitk::PolhemusTrackingDevice::StopTracking() { m_Device->StopTracking(); return Superclass::StopTracking(); } unsigned int mitk::PolhemusTrackingDevice::GetToolCount() const { return (unsigned int)this->m_AllTools.size(); } mitk::TrackingTool* mitk::PolhemusTrackingDevice::GetTool(unsigned int toolNumber) const { if (toolNumber >= this->GetToolCount()) return nullptr; else return this->m_AllTools[toolNumber]; } bool mitk::PolhemusTrackingDevice::OpenConnection() { //reset everything if (m_Device.IsNull()) { m_Device = mitk::PolhemusInterface::New(); } if (!m_Device->Connect()) //Connect the device, if it fails, throw an error. { MITK_ERROR << "Cannot connect Polhemus device!"; CloseConnection(); return false; } //Ready must be set here, 'cause if tools don't match we need to be able to disconnect. this->SetState(Ready); //check if connected ports of Polhemus matches the tools in the toolStorage. std::vector toolPorts = m_Device->GetToolPorts(); //first, check size. if (this->GetToolCount() != toolPorts.size()) { MITK_ERROR << "Cannot connect device, number of tools in toolstorage doesn't match the number of tools connected to Polhemus device!"; CloseConnection(); return false; } //second, check if toolStorage identifier is included in this port. for (auto _tool : m_AllTools) { if (std::find(toolPorts.begin(), toolPorts.end(), _tool->GetToolPort()) == toolPorts.end()) { MITK_ERROR << "Cannot connect device, tool " << _tool->GetToolPort() << " is not connected to its port."; CloseConnection(); return false; } else { //erase this port to avoid that two tools want to connect to the same port. toolPorts.erase(std::find(toolPorts.begin(), toolPorts.end(), _tool->GetToolPort())); } } m_Device->SetHemisphereTrackingEnabled(m_HemisphereTrackingEnabled); return true; } bool mitk::PolhemusTrackingDevice::CloseConnection() { bool returnValue = true; if (this->GetState() == Setup) return true; returnValue = m_Device->Disconnect(); this->SetState(Setup); return returnValue; } mitk::PolhemusInterface* mitk::PolhemusTrackingDevice::GetDevice() { return m_Device; } std::vector mitk::PolhemusTrackingDevice::GetAllTools() { return this->m_AllTools; } void mitk::PolhemusTrackingDevice::TrackTools() { try { /* lock the TrackingFinishedMutex to signal that the execution rights are now transfered to the tracking thread */ - MutexLockHolder trackingFinishedLockHolder(*m_TrackingFinishedMutex); // keep lock until end of scope + std::lock_guard trackingFinishedLockHolder(m_TrackingFinishedMutex); // keep lock until end of scope bool localStopTracking; // Because m_StopTracking is used by two threads, access has to be guarded by a mutex. To minimize thread locking, a local copy is used here - this->m_StopTrackingMutex->Lock(); // update the local copy of m_StopTracking + this->m_StopTrackingMutex.lock(); // update the local copy of m_StopTracking localStopTracking = this->m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); Sleep(100);//Wait a bit until the tracker is ready... while ((this->GetState() == Tracking) && (localStopTracking == false)) { std::vector lastData = this->GetDevice()->GetLastFrame(); if (lastData.size() != m_AllTools.size()) { MITK_WARN << "Tool count is corrupt. Hardware gives " << lastData.size() << " tools, MITK expects " << m_AllTools.size() << " tools. Aborting!"; } else { std::vector allTools = this->GetAllTools(); for (size_t i = 0; i < allTools.size(); i++) { mitk::PolhemusTool::Pointer currentTool = allTools.at(i); const int distortionLevel = lastData.at(i).distortionLevel; if (distortionLevel == 0) { currentTool->SetDataValid(true); } else { currentTool->SetDataValid(false); } currentTool->SetDistortionLevel(distortionLevel); currentTool->SetPosition(lastData.at(i).pos); currentTool->SetOrientation(lastData.at(i).rot); currentTool->SetIGTTimeStamp(mitk::IGTTimeStamp::GetInstance()->GetElapsed()); } } /* Update the local copy of m_StopTracking */ - this->m_StopTrackingMutex->Lock(); + this->m_StopTrackingMutex.lock(); localStopTracking = m_StopTracking; - this->m_StopTrackingMutex->Unlock(); + this->m_StopTrackingMutex.unlock(); } } catch (...) { this->StopTracking(); mitkThrowException(mitk::IGTHardwareException) << "Error while trying to track tools. Thread stopped."; } } -ITK_THREAD_RETURN_TYPE mitk::PolhemusTrackingDevice::ThreadStartTracking(void* pInfoStruct) +void mitk::PolhemusTrackingDevice::ThreadStartTracking() { - /* extract this pointer from Thread Info structure */ - struct itk::MultiThreader::ThreadInfoStruct * pInfo = (struct itk::MultiThreader::ThreadInfoStruct*)pInfoStruct; - if (pInfo == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - if (pInfo->UserData == nullptr) - { - return ITK_THREAD_RETURN_VALUE; - } - PolhemusTrackingDevice *trackingDevice = (PolhemusTrackingDevice*)pInfo->UserData; - - if (trackingDevice != nullptr) - trackingDevice->TrackTools(); - - return ITK_THREAD_RETURN_VALUE; + this->TrackTools(); } bool mitk::PolhemusTrackingDevice::AutoDetectToolsAvailable() { return true; } mitk::NavigationToolStorage::Pointer mitk::PolhemusTrackingDevice::AutoDetectTools() { std::vector singeFrameData = this->m_Device->AutoDetectTools(); MITK_INFO << "Found " << singeFrameData.size() << " tools."; mitk::NavigationToolStorage::Pointer returnValue = mitk::NavigationToolStorage::New(); for each (mitk::PolhemusInterface::trackingData t in singeFrameData) { mitk::NavigationTool::Pointer newTool = mitk::NavigationTool::New(); std::stringstream name; name << "Sensor-" << ((int)t.id); newTool->GetDataNode()->SetName(name.str()); //The identifier defines, which plug is used (e.g. "Sens 2" --> 2). std::stringstream identifier; identifier << ((int)t.id); newTool->SetIdentifier(identifier.str()); newTool->SetTrackingDeviceType(mitk::PolhemusTrackerTypeInformation::GetDeviceDataPolhemusTrackerLiberty().Line); returnValue->AddTool(newTool); } return returnValue; } void mitk::PolhemusTrackingDevice::SetHemisphereTrackingEnabled(bool _HemisphereTrackingEnabled) { //We need to remember if HemisphereTracking is switch on for this reason: /* m_Device->SetHemi works only if the device is connected. However, GUI can also change if it is not connected. In this case, we remember it in the m_HemisphereTrackingEnabled variable. And when connecting, we know, which status is wanted from the user by GUI. */ m_HemisphereTrackingEnabled = _HemisphereTrackingEnabled; this->m_Device->SetHemisphereTrackingEnabled(_HemisphereTrackingEnabled); } void mitk::PolhemusTrackingDevice::ToggleHemisphere(int _tool) { this->m_Device->ToggleHemisphere(_tool); } void mitk::PolhemusTrackingDevice::SetHemisphere(int _tool, mitk::Vector3D _hemisphere) { //If you set a hemisphere vector which is unequal (0|0|0), this means, that there is no hemisphere tracking any more //disable the option, so that it can be reactivated... Also if it is just a single tool. if (_hemisphere.GetNorm() != 0) m_HemisphereTrackingEnabled = false; this->m_Device->SetHemisphere(_tool, _hemisphere); } mitk::Vector3D mitk::PolhemusTrackingDevice::GetHemisphere(int _tool) { return this->m_Device->GetHemisphere(_tool); } bool mitk::PolhemusTrackingDevice::GetHemisphereTrackingEnabled(int _tool) { return this->m_Device->GetHemisphereTrackingEnabled(_tool); } void mitk::PolhemusTrackingDevice::AdjustHemisphere(int _tool) { return this->m_Device->AdjustHemisphere(_tool); } diff --git a/Modules/IGT/TrackingDevices/mitkPolhemusTrackingDevice.h b/Modules/IGT/TrackingDevices/mitkPolhemusTrackingDevice.h index 0041bfa508..19261168a9 100644 --- a/Modules/IGT/TrackingDevices/mitkPolhemusTrackingDevice.h +++ b/Modules/IGT/TrackingDevices/mitkPolhemusTrackingDevice.h @@ -1,161 +1,160 @@ /*============================================================================ 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 MITKPolhemusTRACKINGDEVICE_H_HEADER_INCLUDED_ #define MITKPolhemusTRACKINGDEVICE_H_HEADER_INCLUDED_ #include #include #include #include -#include +#include namespace mitk { /** Documentation: * \brief An object of this class represents Polhemus tracking device. You can add tools to this * device, then open the connection and start tracking. The tracking device will then * continuously update the tool coordinates. * The tools which are used by Polhemus need to be connected to the correct port. * The port of the tool is stored as m_ToolPort in PolhemusTool AND as identifier in the NavigationTool (ToolStorage). * \ingroup IGT */ class MITKIGT_EXPORT PolhemusTrackingDevice : public TrackingDevice { public: mitkClassMacro(PolhemusTrackingDevice, TrackingDevice); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * \brief Starts the tracking. * \return Returns true if the tracking is started. Throws an exception if an error occures. * @throw mitk::IGTHardwareException Throws an exception if there is an error during start tracking. */ virtual bool StartTracking() override; /** * \brief Stops the tracking. * \return Returns true if the tracking is stopped. */ virtual bool StopTracking() override; /** * \brief Opens the connection to the device. This have to be done before the tracking is started. * @throw mitk::IGTHardwareException Throws an exception if there is an error during open connection. */ virtual bool OpenConnection() override; /** * \brief Closes the connection and clears all resources. */ virtual bool CloseConnection() override; /** * \return Returns the number of tools which have been added to the device. */ virtual unsigned int GetToolCount() const override; /** * \param toolNumber The number of the tool which should be given back. * \return Returns the tool which the number "toolNumber". Returns NULL, if there is * no tool with this number. */ TrackingTool* GetTool(unsigned int toolNumber) const override; /** * \brief Create a new Polhemus tool with toolName and add it to the list of tools * * This method will create a new PolhemusTool object, * set the tool name toolName and then add it to the list of tools. * It returns a pointer of type mitk::TrackingTool to the tool * that can be used to read tracking data from it. * This is the only way to add tools to PolhemusTrackingDevice. * * \warning adding tools is not possible in tracking mode, only in setup and ready. */ mitk::TrackingTool* AddTool(const char* toolName, int toolPort); bool IsDeviceInstalled(); /** @return Returns true if this device can autodetects its tools. */ virtual bool AutoDetectToolsAvailable(); /** Autodetects tools from this device and returns them as a navigation tool storage. * @return Returns the detected tools. Returns an empty storage if no tools are present * or if detection is not possible */ virtual mitk::NavigationToolStorage::Pointer AutoDetectTools(); /** Enables/disables hemisphere tracking for all sensors. */ void SetHemisphereTrackingEnabled(bool _HemisphereTrackingEnabled); /** Is Hemisphere Tracking Enabled for this tool? */ bool GetHemisphereTrackingEnabled(int _tool); /** Toggles the current hemisphere. Parameter _tool describes, for which tool the hemisphere should change. Default -1 toggles all tools.*/ void ToggleHemisphere(int _tool = -1); /** Sets the Hemisphere of tool _tool to the vector _hemisphere */ void SetHemisphere(int _tool, mitk::Vector3D _hemisphere); /** Get the Hemisphere for _tool as mitk vector */ mitk::Vector3D GetHemisphere(int _tool); /** Adjust the Hemisphere for this tool. User needs to make sure, that the tool is located in hemisphere (1|0|0) when calling this function. In contrast to SetHemisphere(1,0,0), this method restores the original HemisphereTracking settings at the end. */ void AdjustHemisphere(int _tool); protected: PolhemusTrackingDevice(); ~PolhemusTrackingDevice(); /** * \brief Adds a tool to the tracking device. * * \param tool The tool which will be added. * \return Returns true if the tool has been added, false otherwise. */ bool InternalAddTool(PolhemusTool::Pointer tool); /** * \brief This method tracks tools as long as the variable m_Mode is set to "Tracking". * Tracking tools means grabbing frames from the camera an updating the tools. * @throw mitk::IGTHardwareException Throws an exception if there is an error during tracking of tools. */ void TrackTools(); /** * \return Returns all tools of the tracking device. */ std::vector GetAllTools(); /** * \return Gives back the device which is represented by an object of the class PolhemusInterface. */ PolhemusInterface* GetDevice(); - static ITK_THREAD_RETURN_TYPE ThreadStartTracking(void* data); + void ThreadStartTracking(); std::vector m_AllTools; ///< vector holding all tools PolhemusInterface::Pointer m_Device; ///< represents the interface to the tracking hardware - itk::MultiThreader::Pointer m_MultiThreader; - int m_ThreadID; + std::thread m_Thread; bool m_HemisphereTrackingEnabled; }; }//mitk #endif /* MITKPolhemusTRACKINGDEVICE_H_HEADER_INCLUDED_ */ diff --git a/Modules/MapperExt/files.cmake b/Modules/MapperExt/files.cmake index 01778f6a7a..0c8592ec5a 100644 --- a/Modules/MapperExt/files.cmake +++ b/Modules/MapperExt/files.cmake @@ -1,18 +1,16 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkEnhancedPointSetVtkMapper3D.cpp - mitkGPUVolumeMapper3D.cpp mitkSplineVtkMapper3D.cpp mitkUnstructuredGridMapper2D.cpp mitkUnstructuredGridVtkMapper3D.cpp mitkVectorImageMapper2D.cpp mitkVolumeMapperVtkSmart3D.cpp vtkMaskedGlyph2D.cpp vtkMaskedGlyph3D.cpp - vtkMitkGPUVolumeRayCastMapper.cpp vtkUnstructuredGridMapper.cpp vtkPointSetSlicer.cxx ) diff --git a/Modules/MapperExt/include/mitkGPUVolumeMapper3D.h b/Modules/MapperExt/include/mitkGPUVolumeMapper3D.h deleted file mode 100644 index 46fff93757..0000000000 --- a/Modules/MapperExt/include/mitkGPUVolumeMapper3D.h +++ /dev/null @@ -1,162 +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. - -============================================================================*/ - -#ifndef MITKGPUVOLUMEMAPPER3D_H_HEADER_INCLUDED -#define MITKGPUVOLUMEMAPPER3D_H_HEADER_INCLUDED - -// MITK -#include "MitkMapperExtExports.h" -#include "mitkBaseRenderer.h" -#include "mitkCommon.h" -#include "mitkImage.h" -#include "mitkVtkMapper.h" - -// VTK -#include -#include -#include -#include -#include -#include - -#include "vtkMitkGPUVolumeRayCastMapper.h" - -namespace mitk -{ - /************************************************************************/ - /* Properties that influence the mapper are: - * - * - \b "level window": for the level window of the volume data - * - \b "LookupTable" : for the lookup table of the volume data - * - \b "TransferFunction" (mitk::TransferFunctionProperty): for the used transfer function of the volume data - ************************************************************************/ - - //##Documentation - //## @brief Vtk-based mapper for VolumeData - //## - //## @ingroup Mapper - class MITKMAPPEREXT_EXPORT GPUVolumeMapper3D : public VtkMapper - { - public: - mitkClassMacro(GPUVolumeMapper3D, VtkMapper); - - itkFactorylessNewMacro(Self); - - itkCloneMacro(Self); - - virtual const mitk::Image *GetInput(); - - vtkProp *GetVtkProp(mitk::BaseRenderer *renderer) override; - - void ApplyProperties(vtkActor *actor, mitk::BaseRenderer *renderer) override; - static void SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); - - /** Returns true if this Mapper currently allows for Level-of-Detail rendering. - * This reflects whether this Mapper currently invokes StartEvent, EndEvent, and - * ProgressEvent on BaseRenderer. */ - bool IsLODEnabled(BaseRenderer *renderer = nullptr) const override; - bool IsMIPEnabled(BaseRenderer *renderer = nullptr); - bool IsGPUEnabled(BaseRenderer *renderer = nullptr); - bool IsRAYEnabled(BaseRenderer *renderer = nullptr); - - void MitkRenderVolumetricGeometry(mitk::BaseRenderer *renderer) override; - - protected: - GPUVolumeMapper3D(); - ~GPUVolumeMapper3D() override; - - bool IsRenderable(mitk::BaseRenderer *renderer); - - void InitCommon(); - void DeinitCommon(); - - void InitCPU(mitk::BaseRenderer *renderer); - void DeinitCPU(mitk::BaseRenderer *renderer); - void GenerateDataCPU(mitk::BaseRenderer *renderer); - - bool InitGPU(mitk::BaseRenderer *renderer); - void DeinitGPU(mitk::BaseRenderer *); - void GenerateDataGPU(mitk::BaseRenderer *); - - bool InitRAY(mitk::BaseRenderer *renderer); - void DeinitRAY(mitk::BaseRenderer *renderer); - void GenerateDataRAY(mitk::BaseRenderer *renderer); - - void InitVtkMapper(mitk::BaseRenderer *renderer); - - void GenerateDataForRenderer(mitk::BaseRenderer *renderer) override; - - void CreateDefaultTransferFunctions(); - void UpdateTransferFunctions(mitk::BaseRenderer *renderer); - - vtkSmartPointer m_Volumenullptr; - - bool m_commonInitialized; - vtkSmartPointer m_UnitSpacingImageFilter; - vtkSmartPointer m_DefaultOpacityTransferFunction; - vtkSmartPointer m_DefaultGradientTransferFunction; - vtkSmartPointer m_DefaultColorTransferFunction; - vtkSmartPointer m_BinaryOpacityTransferFunction; - vtkSmartPointer m_BinaryGradientTransferFunction; - vtkSmartPointer m_BinaryColorTransferFunction; - - class LocalStorage : public mitk::Mapper::BaseLocalStorage - { - public: - // NO SMARTPOINTER HERE - vtkRenderWindow *m_VtkRenderWindow; - - bool m_cpuInitialized; - vtkSmartPointer m_VolumeCPU; - vtkSmartPointer m_MapperCPU; - vtkSmartPointer m_VolumePropertyCPU; - - bool m_gpuSupported; - bool m_gpuInitialized; - vtkSmartPointer m_VolumeGPU; - vtkSmartPointer m_VolumePropertyGPU; - - bool m_raySupported; - bool m_rayInitialized; - vtkSmartPointer m_VolumeRAY; - vtkSmartPointer m_MapperRAY; - vtkSmartPointer m_VolumePropertyRAY; - - LocalStorage() - { - m_VtkRenderWindow = nullptr; - - m_cpuInitialized = false; - - m_gpuInitialized = false; - m_gpuSupported = true; // assume initially gpu slicing is supported - - m_rayInitialized = false; - m_raySupported = true; // assume initially gpu raycasting is supported - } - - ~LocalStorage() override - { - if (m_cpuInitialized && m_MapperCPU && m_VtkRenderWindow) - m_MapperCPU->ReleaseGraphicsResources(m_VtkRenderWindow); - - if (m_rayInitialized && m_MapperRAY && m_VtkRenderWindow) - m_MapperRAY->ReleaseGraphicsResources(m_VtkRenderWindow); - } - }; - - mitk::LocalStorageHandler m_LSH; - }; - -} // namespace mitk - -#endif /* MITKVOLUMEDATAVTKMAPPER3D_H_HEADER_INCLUDED */ diff --git a/Modules/MapperExt/include/vtkMitkGPUVolumeRayCastMapper.h b/Modules/MapperExt/include/vtkMitkGPUVolumeRayCastMapper.h deleted file mode 100644 index 7d1bf53711..0000000000 --- a/Modules/MapperExt/include/vtkMitkGPUVolumeRayCastMapper.h +++ /dev/null @@ -1,290 +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. - -============================================================================*/ -/*========================================================================= - - Program: Visualization Toolkit - Module: $RCSfile: vtkMitkGPUVolumeRayCastMapper.h,v $ - - Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen - All rights reserved. - See Copyright.txt or http://www.kitware.com/Copyright.htm for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. See the above copyright notice for more information. - -=========================================================================*/ -// .NAME vtkMitkGPUVolumeRayCastMapper - Ray casting performed on the GPU. -// .SECTION Description -// vtkMitkGPUVolumeRayCastMapper is a volume mapper that performs ray casting on -// the GPU using fragment programs. -// - -#ifndef __vtkMitkGPUVolumeRayCastMapper_h -#define __vtkMitkGPUVolumeRayCastMapper_h - -#include "MitkMapperExtExports.h" -#include "mitkCommon.h" -#include "vtkVersionMacros.h" -#include "vtkVolumeMapper.h" - -class vtkVolumeProperty; -class vtkRenderWindow; - -// class vtkKWAMRVolumeMapper; // friend class. - -class MITKMAPPEREXT_EXPORT vtkMitkGPUVolumeRayCastMapper : public vtkVolumeMapper -{ -public: - vtkTypeMacro(vtkMitkGPUVolumeRayCastMapper, vtkVolumeMapper); - void PrintSelf(ostream &os, vtkIndent indent) override; - - // Description: - // If AutoAdjustSampleDistances is on, the the ImageSampleDistance - // will be varied to achieve the allocated render time of this - // prop (controlled by the desired update rate and any culling in - // use). - vtkSetClampMacro(AutoAdjustSampleDistances, int, 0, 1); - vtkGetMacro(AutoAdjustSampleDistances, int); - vtkBooleanMacro(AutoAdjustSampleDistances, int); - - // Description: - // Set/Get the distance between samples used for rendering - // when AutoAdjustSampleDistances is off, or when this mapper - // has more than 1 second allocated to it for rendering. - // Initial value is 1.0. - vtkSetMacro(SampleDistance, float); - vtkGetMacro(SampleDistance, float); - - // Description: - // Sampling distance in the XY image dimensions. Default value of 1 meaning - // 1 ray cast per pixel. If set to 0.5, 4 rays will be cast per pixel. If - // set to 2.0, 1 ray will be cast for every 4 (2 by 2) pixels. This value - // will be adjusted to meet a desired frame rate when AutoAdjustSampleDistances - // is on. - vtkSetClampMacro(ImageSampleDistance, float, 0.1f, 100.0f); - vtkGetMacro(ImageSampleDistance, float); - - // Description: - // This is the minimum image sample distance allow when the image - // sample distance is being automatically adjusted. - vtkSetClampMacro(MinimumImageSampleDistance, float, 0.1f, 100.0f); - vtkGetMacro(MinimumImageSampleDistance, float); - - // Description: - // This is the maximum image sample distance allow when the image - // sample distance is being automatically adjusted. - vtkSetClampMacro(MaximumImageSampleDistance, float, 0.1f, 100.0f); - vtkGetMacro(MaximumImageSampleDistance, float); - - // Description: - // Set/Get the window / level applied to the final color. - // This allows brightness / contrast adjustments on the - // final image. - // window is the width of the window. - // level is the center of the window. - // Initial window value is 1.0 - // Initial level value is 0.5 - // window cannot be null but can be negative, this way - // values will be reversed. - // |window| can be larger than 1.0 - // level can be any real value. - vtkSetMacro(FinalColorWindow, float); - vtkGetMacro(FinalColorWindow, float); - vtkSetMacro(FinalColorLevel, float); - vtkGetMacro(FinalColorLevel, float); - - // Description: - // Maximum size of the 3D texture in GPU memory. - // Will default to the size computed from the graphics - // card. Can be adjusted by the user. - vtkSetMacro(MaxMemoryInBytes, vtkIdType); - vtkGetMacro(MaxMemoryInBytes, vtkIdType); - - // Description: - // Maximum fraction of the MaxMemoryInBytes that should - // be used to hold the texture. Valid values are 0.1 to - // 1.0. - vtkSetClampMacro(MaxMemoryFraction, float, 0.1f, 1.0f); - vtkGetMacro(MaxMemoryFraction, float); - - // Description: - // Tells if the mapper will report intermediate progress. - // Initial value is true. As the progress works with a GL blocking - // call (glFinish()), this can be useful for huge dataset but can - // slow down rendering of small dataset. It should be set to true - // for big dataset or complex shading and streaming but to false for - // small datasets. - vtkSetMacro(ReportProgress, bool); - vtkGetMacro(ReportProgress, bool); - - // Description: - // Based on hardware and properties, we may or may not be able to - // render using 3D texture mapping. This indicates if 3D texture - // mapping is supported by the hardware, and if the other extensions - // necessary to support the specific properties are available. - virtual int IsRenderSupported(vtkRenderWindow *vtkNotUsed(window), vtkVolumeProperty *vtkNotUsed(property)) - { - return 0; - } - - void CreateCanonicalView(vtkRenderer *ren, - vtkVolume *volume, - vtkImageData *image, - int blend_mode, - double viewDirection[3], - double viewUp[3]); - - void SetMaskInput(vtkImageData *mask); - vtkGetObjectMacro(MaskInput, vtkImageData); - - // Description: - // Tells how much mask color transfer function is used compared to the - // standard color transfer function when the mask is true. - // 0.0 means only standard color transfer function. - // 1.0 means only mask color tranfer function. - // Initial value is 1.0. - vtkSetClampMacro(MaskBlendFactor, float, 0.0f, 1.0f); - vtkGetMacro(MaskBlendFactor, float); - - // BTX - // Description: - // WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE - // Initialize rendering for this volume. - void Render(vtkRenderer *, vtkVolume *) override; - - // Description: - // Handled in the subclass - the actual render method - // \pre input is up-to-date. - virtual void GPURender(vtkRenderer *, vtkVolume *) {} - // Description: - // WARNING: INTERNAL METHOD - NOT INTENDED FOR GENERAL USE - // Release any graphics resources that are being consumed by this mapper. - // The parameter window could be used to determine which graphic - // resources to release. - // \deprecatedSince{2013_12} - DEPRECATED(void ReleaseGraphicsResources(vtkWindow *) override){}; - - // Description: - // Return how much the dataset has to be reduced in each dimension to - // fit on the GPU. If the value is 1.0, there is no need to reduce the - // dataset. - // \pre the calling thread has a current OpenGL context. - // \pre mapper_supported: IsRenderSupported(renderer->GetRenderWindow(),0) - // The computation is based on hardware limits (3D texture indexable size) - // and MaxMemoryInBytes. - // \post valid_i_ratio: ratio[0]>0 && ratio[0]<=1.0 - // \post valid_j_ratio: ratio[1]>0 && ratio[1]<=1.0 - // \post valid_k_ratio: ratio[2]>0 && ratio[2]<=1.0 - virtual void GetReductionRatio(double ratio[3]) = 0; - - // ETX - -protected: - vtkMitkGPUVolumeRayCastMapper(); - ~vtkMitkGPUVolumeRayCastMapper() override; - - // Check to see that the render will be OK - int ValidateRender(vtkRenderer *, vtkVolume *); - - // Special version of render called during the creation - // of a canonical view. - void CanonicalViewRender(vtkRenderer *, vtkVolume *); - - // Methods called by the AMR Volume Mapper. - virtual void PreRender(vtkRenderer *ren, - vtkVolume *vol, - double datasetBounds[6], - double scalarRange[2], - int numberOfScalarComponents, - unsigned int numberOfLevels) = 0; - - // \pre input is up-to-date - virtual void RenderBlock(vtkRenderer *ren, vtkVolume *vol, unsigned int level) = 0; - - virtual void PostRender(vtkRenderer *ren, int numberOfScalarComponents) = 0; - - // Description: - // Called by the AMR Volume Mapper. - // Set the flag that tells if the scalars are on point data (0) or - // cell data (1). - void SetCellFlag(int cellFlag); - - // The distance between sample points along the ray - float SampleDistance; - - float ImageSampleDistance; - float MinimumImageSampleDistance; - float MaximumImageSampleDistance; - int AutoAdjustSampleDistances; - - int SmallVolumeRender; - double BigTimeToDraw; - double SmallTimeToDraw; - - float FinalColorWindow; - float FinalColorLevel; - - vtkIdType MaxMemoryInBytes; - float MaxMemoryFraction; - - // 1 if we are generating the canonical image, 0 otherwise - int GeneratingCanonicalView; - vtkImageData *CanonicalViewImageData; - - // Description: - // Set the mapper in AMR Mode or not. Initial value is false. - // Called only by the vtkKWAMRVolumeMapper - vtkSetClampMacro(AMRMode, int, 0, 1); - vtkGetMacro(AMRMode, int); - vtkBooleanMacro(AMRMode, int); - - int AMRMode; - int CellFlag; // point data or cell data (or field data, not handled) ? - - // Description: - // Compute the cropping planes clipped by the bounds of the volume. - // The result is put into this->ClippedCroppingRegionPlanes. - // NOTE: IT WILL BE MOVED UP TO vtkVolumeMapper after bullet proof usage - // in this mapper. Other subclasses will use the ClippedCroppingRegionsPlanes - // members instead of CroppingRegionPlanes. - // \pre volume_exists: this->GetInput()!=0 - // \pre valid_cropping: this->Cropping && - // this->CroppingRegionPlanes[0]CroppingRegionPlanes[1] && - // this->CroppingRegionPlanes[2]CroppingRegionPlanes[3] && - // this->CroppingRegionPlanes[4]CroppingRegionPlanes[5]) - virtual void ClipCroppingRegionPlanes(); - - double ClippedCroppingRegionPlanes[6]; - - bool ReportProgress; - - vtkImageData *MaskInput; - - float MaskBlendFactor; - - vtkGetObjectMacro(TransformedInput, vtkImageData); - void SetTransformedInput(vtkImageData *); - - vtkImageData *TransformedInput; - - // Description: - // This is needed only to check if the input data has been changed since the last - // Render() call. - vtkImageData *LastInput; - -private: - vtkMitkGPUVolumeRayCastMapper(const vtkMitkGPUVolumeRayCastMapper &); // Not implemented. - void operator=(const vtkMitkGPUVolumeRayCastMapper &); // Not implemented. -}; - -#endif diff --git a/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp b/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp deleted file mode 100644 index b339cc0dfc..0000000000 --- a/Modules/MapperExt/src/mitkGPUVolumeMapper3D.cpp +++ /dev/null @@ -1,586 +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. - -============================================================================*/ - -#define GPU_INFO MITK_INFO("mapper.vr") -#define GPU_WARN MITK_WARN("mapper.vr") -#define GPU_ERROR MITK_ERROR("mapper.vr") - -#include "mitkGPUVolumeMapper3D.h" - -#include "mitkDataNode.h" - -#include "mitkColorProperty.h" -#include "mitkColorProperty.h" -#include "mitkLevelWindow.h" -#include "mitkLevelWindowProperty.h" -#include "mitkLookupTableProperty.h" -#include "mitkProperties.h" -#include "mitkRenderingManager.h" -#include "mitkTransferFunctionInitializer.h" -#include "mitkTransferFunctionProperty.h" -#include "mitkVtkPropRenderer.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include "vtkMitkGPUVolumeRayCastMapper.h" - -#include "vtkOpenGLGPUVolumeRayCastMapper.h" - -const mitk::Image *mitk::GPUVolumeMapper3D::GetInput() -{ - return static_cast(GetDataNode()->GetData()); -} - -void mitk::GPUVolumeMapper3D::MitkRenderVolumetricGeometry(mitk::BaseRenderer *renderer) -{ - VtkMapper::MitkRenderVolumetricGeometry(renderer); - - // if(ls->m_gpuInitialized) - // ls->m_MapperGPU->UpdateMTime(); -} - -bool mitk::GPUVolumeMapper3D::InitGPU(mitk::BaseRenderer * /*renderer*/) -{ - return false; -} - -void mitk::GPUVolumeMapper3D::InitCPU(mitk::BaseRenderer *renderer) -{ - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - - if (ls->m_cpuInitialized) - return; - - ls->m_VtkRenderWindow = renderer->GetVtkRenderer()->GetRenderWindow(); - - ls->m_MapperCPU = vtkSmartPointer::New(); - int numThreads = ls->m_MapperCPU->GetNumberOfThreads(); - - GPU_INFO << "initializing cpu-raycast-vr (vtkFixedPointVolumeRayCastMapper) (" << numThreads << " threads)"; - - ls->m_MapperCPU->SetSampleDistance(1.0); - // ls->m_MapperCPU->LockSampleDistanceToInputSpacingOn(); - ls->m_MapperCPU->SetImageSampleDistance(1.0); - ls->m_MapperCPU->IntermixIntersectingGeometryOn(); - ls->m_MapperCPU->SetAutoAdjustSampleDistances(0); - - ls->m_VolumePropertyCPU = vtkSmartPointer::New(); - ls->m_VolumePropertyCPU->ShadeOn(); - ls->m_VolumePropertyCPU->SetAmbient(0.10f); // 0.05f - ls->m_VolumePropertyCPU->SetDiffuse(0.50f); // 0.45f - ls->m_VolumePropertyCPU->SetSpecular(0.40f); // 0.50f - ls->m_VolumePropertyCPU->SetSpecularPower(16.0f); - ls->m_VolumePropertyCPU->SetInterpolationTypeToLinear(); - - ls->m_VolumeCPU = vtkSmartPointer::New(); - ls->m_VolumeCPU->SetMapper(ls->m_MapperCPU); - ls->m_VolumeCPU->SetProperty(ls->m_VolumePropertyCPU); - ls->m_VolumeCPU->VisibilityOn(); - - ls->m_MapperCPU->SetInputConnection(m_UnitSpacingImageFilter->GetOutputPort()); // m_Resampler->GetOutput()); - - ls->m_cpuInitialized = true; -} - -void mitk::GPUVolumeMapper3D::DeinitCPU(mitk::BaseRenderer *renderer) -{ - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - - if (!ls->m_cpuInitialized) - return; - - GPU_INFO << "deinitializing cpu-raycast-vr"; - - ls->m_VolumePropertyCPU = nullptr; - ls->m_MapperCPU = nullptr; - ls->m_VolumeCPU = nullptr; - ls->m_cpuInitialized = false; -} - -mitk::GPUVolumeMapper3D::GPUVolumeMapper3D() -{ - m_Volumenullptr = nullptr; - m_commonInitialized = false; -} - -mitk::GPUVolumeMapper3D::~GPUVolumeMapper3D() -{ - DeinitCommon(); -} - -void mitk::GPUVolumeMapper3D::InitCommon() -{ - if (m_commonInitialized) - return; - - m_UnitSpacingImageFilter = vtkSmartPointer::New(); - m_UnitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0); - - CreateDefaultTransferFunctions(); - - m_commonInitialized = true; -} - -void mitk::GPUVolumeMapper3D::DeinitCommon() -{ - if (!m_commonInitialized) - return; - - m_commonInitialized = false; -} - -bool mitk::GPUVolumeMapper3D::IsRenderable(mitk::BaseRenderer *renderer) -{ - if (!GetDataNode()) - return false; - - DataNode *node = GetDataNode(); - - bool visible = true; - node->GetVisibility(visible, renderer, "visible"); - - if (!visible) - return false; - - bool value = false; - if (!node->GetBoolProperty("volumerendering", value, renderer)) - return false; - - if (!value) - return false; - - auto *input = const_cast(this->GetInput()); - - if (!input || !input->IsInitialized()) - return false; - - vtkImageData *inputData = input->GetVtkImageData(this->GetTimestep()); - - if (inputData == nullptr) - return false; - - return true; -} - -void mitk::GPUVolumeMapper3D::InitVtkMapper(mitk::BaseRenderer *renderer) -{ - if (IsRAYEnabled(renderer)) - { - DeinitCPU(renderer); - if (!InitRAY(renderer)) - { - GPU_WARN << "hardware renderer can't initialize ... falling back to software renderer"; - goto fallback; - } - } - else if (IsGPUEnabled(renderer)) - { - DeinitCPU(renderer); - DeinitRAY(renderer); - if (!InitGPU(renderer)) - { - GPU_WARN << "hardware renderer can't initialize ... falling back to software renderer"; - goto fallback; - } - } - else - { - fallback: - DeinitRAY(renderer); - InitCPU(renderer); - } -} - -vtkProp *mitk::GPUVolumeMapper3D::GetVtkProp(mitk::BaseRenderer *renderer) -{ - if (!IsRenderable(renderer)) - { - if (!m_Volumenullptr) - { - m_Volumenullptr = vtkSmartPointer::New(); - m_Volumenullptr->VisibilityOff(); - } - return m_Volumenullptr; - } - - InitCommon(); - InitVtkMapper(renderer); - - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - - if (ls->m_rayInitialized) - return ls->m_VolumeRAY; - - if (ls->m_gpuInitialized) - return ls->m_VolumeGPU; - - return ls->m_VolumeCPU; -} - -void mitk::GPUVolumeMapper3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) -{ - if (!IsRenderable(renderer)) - return; - - InitCommon(); - InitVtkMapper(renderer); - - auto *input = const_cast(this->GetInput()); - vtkImageData *inputData = input->GetVtkImageData(this->GetTimestep()); - m_UnitSpacingImageFilter->SetInputData(inputData); - - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - - if (ls->m_rayInitialized) - { - GenerateDataRAY(renderer); - } - else - - if (ls->m_gpuInitialized) - { - GenerateDataGPU(renderer); - } - else - { - GenerateDataCPU(renderer); - } - - // UpdateTransferFunctions - UpdateTransferFunctions(renderer); -} - -void mitk::GPUVolumeMapper3D::GenerateDataGPU(mitk::BaseRenderer * /*renderer*/) -{ -} - -void mitk::GPUVolumeMapper3D::GenerateDataCPU(mitk::BaseRenderer *renderer) -{ - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - - int nextLod = mitk::RenderingManager::GetInstance()->GetNextLOD(renderer); - - if (IsLODEnabled(renderer) && nextLod == 0) - { - ls->m_MapperCPU->SetImageSampleDistance(3.5); - ls->m_MapperCPU->SetSampleDistance(1.25); - ls->m_VolumePropertyCPU->SetInterpolationTypeToNearest(); - } - else - { - ls->m_MapperCPU->SetImageSampleDistance(1.0); - ls->m_MapperCPU->SetSampleDistance(1.0); - ls->m_VolumePropertyCPU->SetInterpolationTypeToLinear(); - } - - // Check raycasting mode - if (IsMIPEnabled(renderer)) - ls->m_MapperCPU->SetBlendModeToMaximumIntensity(); - else - ls->m_MapperCPU->SetBlendModeToComposite(); - - // Updating shadings - { - float value = 0; - if (GetDataNode()->GetFloatProperty("volumerendering.cpu.ambient", value, renderer)) - ls->m_VolumePropertyCPU->SetAmbient(value); - if (GetDataNode()->GetFloatProperty("volumerendering.cpu.diffuse", value, renderer)) - ls->m_VolumePropertyCPU->SetDiffuse(value); - if (GetDataNode()->GetFloatProperty("volumerendering.cpu.specular", value, renderer)) - ls->m_VolumePropertyCPU->SetSpecular(value); - if (GetDataNode()->GetFloatProperty("volumerendering.cpu.specular.power", value, renderer)) - ls->m_VolumePropertyCPU->SetSpecularPower(value); - } -} - -void mitk::GPUVolumeMapper3D::CreateDefaultTransferFunctions() -{ - m_DefaultOpacityTransferFunction = vtkSmartPointer::New(); - m_DefaultOpacityTransferFunction->AddPoint(0.0, 0.0); - m_DefaultOpacityTransferFunction->AddPoint(255.0, 0.8); - m_DefaultOpacityTransferFunction->ClampingOn(); - - m_DefaultGradientTransferFunction = vtkSmartPointer::New(); - m_DefaultGradientTransferFunction->AddPoint(0.0, 0.0); - m_DefaultGradientTransferFunction->AddPoint(255.0, 0.8); - m_DefaultGradientTransferFunction->ClampingOn(); - - m_DefaultColorTransferFunction = vtkSmartPointer::New(); - m_DefaultColorTransferFunction->AddRGBPoint(0.0, 0.0, 0.0, 0.0); - m_DefaultColorTransferFunction->AddRGBPoint(127.5, 1, 1, 0.0); - m_DefaultColorTransferFunction->AddRGBPoint(255.0, 0.8, 0.2, 0); - m_DefaultColorTransferFunction->ClampingOn(); - - m_BinaryOpacityTransferFunction = vtkSmartPointer::New(); - m_BinaryOpacityTransferFunction->AddPoint(0, 0.0); - m_BinaryOpacityTransferFunction->AddPoint(1, 1.0); - - m_BinaryGradientTransferFunction = vtkSmartPointer::New(); - m_BinaryGradientTransferFunction->AddPoint(0.0, 1.0); - - m_BinaryColorTransferFunction = vtkSmartPointer::New(); -} - -void mitk::GPUVolumeMapper3D::UpdateTransferFunctions(mitk::BaseRenderer *renderer) -{ - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - - vtkPiecewiseFunction *opacityTransferFunction = m_DefaultOpacityTransferFunction; - vtkPiecewiseFunction *gradientTransferFunction = m_DefaultGradientTransferFunction; - vtkColorTransferFunction *colorTransferFunction = m_DefaultColorTransferFunction; - - bool isBinary = false; - - GetDataNode()->GetBoolProperty("binary", isBinary, renderer); - - if (isBinary) - { - opacityTransferFunction = m_BinaryOpacityTransferFunction; - gradientTransferFunction = m_BinaryGradientTransferFunction; - colorTransferFunction = m_BinaryColorTransferFunction; - - colorTransferFunction->RemoveAllPoints(); - float rgb[3]; - if (!GetDataNode()->GetColor(rgb, renderer)) - rgb[0] = rgb[1] = rgb[2] = 1; - colorTransferFunction->AddRGBPoint(0, rgb[0], rgb[1], rgb[2]); - colorTransferFunction->Modified(); - } - else - { - auto *transferFunctionProp = - dynamic_cast(this->GetDataNode()->GetProperty("TransferFunction", renderer)); - - if (transferFunctionProp) - { - opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction(); - gradientTransferFunction = transferFunctionProp->GetValue()->GetGradientOpacityFunction(); - colorTransferFunction = transferFunctionProp->GetValue()->GetColorTransferFunction(); - } - } - - if (ls->m_gpuInitialized) - { - ls->m_VolumePropertyGPU->SetColor(colorTransferFunction); - ls->m_VolumePropertyGPU->SetScalarOpacity(opacityTransferFunction); - ls->m_VolumePropertyGPU->SetGradientOpacity(gradientTransferFunction); - } - - if (ls->m_rayInitialized) - { - ls->m_VolumePropertyRAY->SetColor(colorTransferFunction); - ls->m_VolumePropertyRAY->SetScalarOpacity(opacityTransferFunction); - ls->m_VolumePropertyRAY->SetGradientOpacity(gradientTransferFunction); - } - - if (ls->m_cpuInitialized) - { - ls->m_VolumePropertyCPU->SetColor(colorTransferFunction); - ls->m_VolumePropertyCPU->SetScalarOpacity(opacityTransferFunction); - ls->m_VolumePropertyCPU->SetGradientOpacity(gradientTransferFunction); - } -} - -void mitk::GPUVolumeMapper3D::ApplyProperties(vtkActor * /*actor*/, mitk::BaseRenderer * /*renderer*/) -{ - // GPU_INFO << "ApplyProperties"; -} - -void mitk::GPUVolumeMapper3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) -{ - // GPU_INFO << "SetDefaultProperties"; - - node->AddProperty("volumerendering", mitk::BoolProperty::New(false), renderer, overwrite); - node->AddProperty("volumerendering.usemip", mitk::BoolProperty::New(false), renderer, overwrite); - node->AddProperty("volumerendering.uselod", mitk::BoolProperty::New(false), renderer, overwrite); - - node->AddProperty("volumerendering.cpu.ambient", mitk::FloatProperty::New(0.10f), renderer, overwrite); - node->AddProperty("volumerendering.cpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); - node->AddProperty("volumerendering.cpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); - node->AddProperty("volumerendering.cpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); - bool usegpu = true; -#ifdef __APPLE__ - usegpu = false; - node->AddProperty("volumerendering.uselod", mitk::BoolProperty::New(true), renderer, overwrite); -#endif - node->AddProperty("volumerendering.usegpu", mitk::BoolProperty::New(usegpu), renderer, overwrite); - - node->AddProperty("volumerendering.useray", mitk::BoolProperty::New(false), renderer, overwrite); - - node->AddProperty("volumerendering.ray.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); - node->AddProperty("volumerendering.ray.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); - node->AddProperty("volumerendering.ray.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); - node->AddProperty("volumerendering.ray.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); - - node->AddProperty("volumerendering.gpu.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); - node->AddProperty("volumerendering.gpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); - node->AddProperty("volumerendering.gpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); - node->AddProperty("volumerendering.gpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); - node->AddProperty("volumerendering.gpu.usetexturecompression", mitk::BoolProperty::New(false), renderer, overwrite); - node->AddProperty("volumerendering.gpu.reducesliceartifacts", mitk::BoolProperty::New(false), renderer, overwrite); - - node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); - - mitk::Image::Pointer image = dynamic_cast(node->GetData()); - if (image.IsNotNull() && image->IsInitialized()) - { - if ((overwrite) || (node->GetProperty("levelwindow", renderer) == nullptr)) - { - mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); - mitk::LevelWindow levelwindow; - levelwindow.SetAuto(image); - levWinProp->SetLevelWindow(levelwindow); - node->SetProperty("levelwindow", levWinProp, renderer); - } - - if ((overwrite) || (node->GetProperty("TransferFunction", renderer) == nullptr)) - { - // add a default transfer function - mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); - mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(tf); - tfInit->SetTransferFunctionMode(0); - node->SetProperty("TransferFunction", mitk::TransferFunctionProperty::New(tf.GetPointer())); - } - } - - Superclass::SetDefaultProperties(node, renderer, overwrite); -} - -bool mitk::GPUVolumeMapper3D::IsLODEnabled(mitk::BaseRenderer *renderer) const -{ - bool value = false; - return GetDataNode()->GetBoolProperty("volumerendering.uselod", value, renderer) && value; -} - -bool mitk::GPUVolumeMapper3D::IsMIPEnabled(mitk::BaseRenderer *renderer) -{ - bool value = false; - return GetDataNode()->GetBoolProperty("volumerendering.usemip", value, renderer) && value; -} - -bool mitk::GPUVolumeMapper3D::IsGPUEnabled(mitk::BaseRenderer *renderer) -{ - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - bool value = false; - return ls->m_gpuSupported && GetDataNode()->GetBoolProperty("volumerendering.usegpu", value, renderer) && value; -} - -bool mitk::GPUVolumeMapper3D::InitRAY(mitk::BaseRenderer *renderer) -{ - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - - if (ls->m_rayInitialized) - return ls->m_raySupported; - - ls->m_VtkRenderWindow = renderer->GetVtkRenderer()->GetRenderWindow(); - - GPU_INFO << "initializing gpu-raycast-vr (vtkOpenGLGPUVolumeRayCastMapper)"; - - ls->m_MapperRAY = vtkSmartPointer::New(); - ls->m_MapperRAY->SetAutoAdjustSampleDistances(0); - ls->m_MapperRAY->SetSampleDistance(1.0); - - ls->m_VolumePropertyRAY = vtkSmartPointer::New(); - ls->m_VolumePropertyRAY->ShadeOn(); - ls->m_VolumePropertyRAY->SetAmbient(0.25f); // 0.05f - ls->m_VolumePropertyRAY->SetDiffuse(0.50f); // 0.45f - ls->m_VolumePropertyRAY->SetSpecular(0.40f); // 0.50f - ls->m_VolumePropertyRAY->SetSpecularPower(16.0f); - ls->m_VolumePropertyRAY->SetInterpolationTypeToLinear(); - - ls->m_VolumeRAY = vtkSmartPointer::New(); - ls->m_VolumeRAY->SetMapper(ls->m_MapperRAY); - ls->m_VolumeRAY->SetProperty(ls->m_VolumePropertyRAY); - ls->m_VolumeRAY->VisibilityOn(); - - ls->m_MapperRAY->SetInputConnection(this->m_UnitSpacingImageFilter->GetOutputPort()); - - ls->m_raySupported = ls->m_MapperRAY->IsRenderSupported(renderer->GetRenderWindow(), ls->m_VolumePropertyRAY); - - ls->m_rayInitialized = true; - return ls->m_raySupported; -} - -void mitk::GPUVolumeMapper3D::DeinitRAY(mitk::BaseRenderer *renderer) -{ - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - - if (ls->m_rayInitialized) - { - GPU_INFO << "deinitializing gpu-raycast-vr"; - - ls->m_MapperRAY = nullptr; - ls->m_VolumePropertyRAY = nullptr; - // Here ReleaseGraphicsResources has to be called to avoid VTK error messages. - // This seems like a VTK bug, because ReleaseGraphicsResources() is ment for internal use, - // but you cannot just delete the object (last smartpointer reference) without getting the - // VTK error. - ls->m_VolumeRAY->ReleaseGraphicsResources(renderer->GetVtkRenderer()->GetRenderWindow()); - ls->m_VolumeRAY = nullptr; - ls->m_rayInitialized = false; - } -} - -void mitk::GPUVolumeMapper3D::GenerateDataRAY(mitk::BaseRenderer *renderer) -{ - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - - if (IsLODEnabled(renderer) && mitk::RenderingManager::GetInstance()->GetNextLOD(renderer) == 0) - ls->m_MapperRAY->SetImageSampleDistance(4.0); - else - ls->m_MapperRAY->SetImageSampleDistance(1.0); - - // Check raycasting mode - if (IsMIPEnabled(renderer)) - ls->m_MapperRAY->SetBlendModeToMaximumIntensity(); - else - ls->m_MapperRAY->SetBlendModeToComposite(); - - // Updating shadings - { - float value = 0; - if (GetDataNode()->GetFloatProperty("volumerendering.ray.ambient", value, renderer)) - ls->m_VolumePropertyRAY->SetAmbient(value); - if (GetDataNode()->GetFloatProperty("volumerendering.ray.diffuse", value, renderer)) - ls->m_VolumePropertyRAY->SetDiffuse(value); - if (GetDataNode()->GetFloatProperty("volumerendering.ray.specular", value, renderer)) - ls->m_VolumePropertyRAY->SetSpecular(value); - if (GetDataNode()->GetFloatProperty("volumerendering.ray.specular.power", value, renderer)) - ls->m_VolumePropertyRAY->SetSpecularPower(value); - } -} - -bool mitk::GPUVolumeMapper3D::IsRAYEnabled(mitk::BaseRenderer *renderer) -{ - LocalStorage *ls = m_LSH.GetLocalStorage(renderer); - bool value = false; - return ls->m_raySupported && GetDataNode()->GetBoolProperty("volumerendering.useray", value, renderer) && value; -} diff --git a/Modules/MapperExt/src/mitkVolumeMapperVtkSmart3D.cpp b/Modules/MapperExt/src/mitkVolumeMapperVtkSmart3D.cpp index 03cd67e759..d1b12197ee 100644 --- a/Modules/MapperExt/src/mitkVolumeMapperVtkSmart3D.cpp +++ b/Modules/MapperExt/src/mitkVolumeMapperVtkSmart3D.cpp @@ -1,236 +1,201 @@ /*============================================================================ 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 "mitkVolumeMapperVtkSmart3D.h" #include "mitkTransferFunctionProperty.h" #include "mitkTransferFunctionInitializer.h" #include "mitkLevelWindowProperty.h" #include #include #include #include void mitk::VolumeMapperVtkSmart3D::GenerateDataForRenderer(mitk::BaseRenderer *renderer) { bool value; this->GetDataNode()->GetBoolProperty("volumerendering", value, renderer); if (!value) { m_Volume->VisibilityOff(); return; } else { createMapper(GetInputImage()); m_Volume->VisibilityOn(); } UpdateTransferFunctions(renderer); UpdateRenderMode(renderer); this->Modified(); } vtkProp* mitk::VolumeMapperVtkSmart3D::GetVtkProp(mitk::BaseRenderer *) { if (!m_Volume->GetMapper()) { createMapper(GetInputImage()); createVolume(); createVolumeProperty(); } return m_Volume; } void mitk::VolumeMapperVtkSmart3D::ApplyProperties(vtkActor *, mitk::BaseRenderer *) { } void mitk::VolumeMapperVtkSmart3D::SetDefaultProperties(mitk::DataNode *node, mitk::BaseRenderer *renderer, bool overwrite) { // GPU_INFO << "SetDefaultProperties"; node->AddProperty("volumerendering", mitk::BoolProperty::New(false), renderer, overwrite); - node->AddProperty("volumerendering.usemip", mitk::BoolProperty::New(false), renderer, overwrite); - node->AddProperty("volumerendering.cpu.ambient", mitk::FloatProperty::New(0.10f), renderer, overwrite); - node->AddProperty("volumerendering.cpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); - node->AddProperty("volumerendering.cpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); - node->AddProperty("volumerendering.cpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); - node->AddProperty("volumerendering.usegpu", mitk::BoolProperty::New(false), renderer, overwrite); - node->AddProperty("volumerendering.useray", mitk::BoolProperty::New(false), renderer, overwrite); - - node->AddProperty("volumerendering.gpu.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); - node->AddProperty("volumerendering.gpu.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); - node->AddProperty("volumerendering.gpu.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); - node->AddProperty("volumerendering.gpu.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); + node->AddProperty("volumerendering.ambient", mitk::FloatProperty::New(0.25f), renderer, overwrite); + node->AddProperty("volumerendering.diffuse", mitk::FloatProperty::New(0.50f), renderer, overwrite); + node->AddProperty("volumerendering.specular", mitk::FloatProperty::New(0.40f), renderer, overwrite); + node->AddProperty("volumerendering.specular.power", mitk::FloatProperty::New(16.0f), renderer, overwrite); node->AddProperty("binary", mitk::BoolProperty::New(false), renderer, overwrite); mitk::Image::Pointer image = dynamic_cast(node->GetData()); if (image.IsNotNull() && image->IsInitialized()) { if ((overwrite) || (node->GetProperty("TransferFunction", renderer) == nullptr)) { // add a default transfer function mitk::TransferFunction::Pointer tf = mitk::TransferFunction::New(); mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(tf); tfInit->SetTransferFunctionMode(0); node->SetProperty("TransferFunction", mitk::TransferFunctionProperty::New(tf.GetPointer())); } } Superclass::SetDefaultProperties(node, renderer, overwrite); } vtkImageData* mitk::VolumeMapperVtkSmart3D::GetInputImage() { auto input = dynamic_cast(this->GetDataNode()->GetData()); return input->GetVtkImageData(this->GetTimestep()); } void mitk::VolumeMapperVtkSmart3D::createMapper(vtkImageData* imageData) { Vector3D spacing; FillVector3D(spacing, 1.0, 1.0, 1.0); m_ImageChangeInformation->SetInputData(imageData); m_ImageChangeInformation->SetOutputSpacing(spacing.GetDataPointer()); m_SmartVolumeMapper->SetBlendModeToComposite(); m_SmartVolumeMapper->SetInputConnection(m_ImageChangeInformation->GetOutputPort()); } void mitk::VolumeMapperVtkSmart3D::createVolume() { m_Volume->SetMapper(m_SmartVolumeMapper); m_Volume->SetProperty(m_VolumeProperty); } void mitk::VolumeMapperVtkSmart3D::createVolumeProperty() { m_VolumeProperty->ShadeOn(); - m_VolumeProperty->SetInterpolationType(VTK_LINEAR_INTERPOLATION); + m_VolumeProperty->SetInterpolationType(VTK_CUBIC_INTERPOLATION); } void mitk::VolumeMapperVtkSmart3D::UpdateTransferFunctions(mitk::BaseRenderer *renderer) { vtkSmartPointer opacityTransferFunction; vtkSmartPointer gradientTransferFunction; vtkSmartPointer colorTransferFunction; bool isBinary = false; this->GetDataNode()->GetBoolProperty("binary", isBinary, renderer); if (isBinary) { colorTransferFunction = vtkSmartPointer::New(); float rgb[3]; if (!GetDataNode()->GetColor(rgb, renderer)) rgb[0] = rgb[1] = rgb[2] = 1; colorTransferFunction->AddRGBPoint(0, rgb[0], rgb[1], rgb[2]); colorTransferFunction->Modified(); opacityTransferFunction = vtkSmartPointer::New(); gradientTransferFunction = vtkSmartPointer::New(); } else { auto *transferFunctionProp = dynamic_cast(this->GetDataNode()->GetProperty("TransferFunction", renderer)); if (transferFunctionProp) { opacityTransferFunction = transferFunctionProp->GetValue()->GetScalarOpacityFunction(); gradientTransferFunction = transferFunctionProp->GetValue()->GetGradientOpacityFunction(); colorTransferFunction = transferFunctionProp->GetValue()->GetColorTransferFunction(); } else { opacityTransferFunction = vtkSmartPointer::New(); gradientTransferFunction = vtkSmartPointer::New(); colorTransferFunction = vtkSmartPointer::New(); } } m_VolumeProperty->SetColor(colorTransferFunction); m_VolumeProperty->SetScalarOpacity(opacityTransferFunction); m_VolumeProperty->SetGradientOpacity(gradientTransferFunction); } void mitk::VolumeMapperVtkSmart3D::UpdateRenderMode(mitk::BaseRenderer *renderer) { - bool usegpu = false; - bool useray = false; - bool usemip = false; - this->GetDataNode()->GetBoolProperty("volumerendering.usegpu", usegpu); - this->GetDataNode()->GetBoolProperty("volumerendering.useray", useray); - this->GetDataNode()->GetBoolProperty("volumerendering.usemip", usemip); - - if (usegpu) - m_SmartVolumeMapper->SetRequestedRenderModeToGPU(); - else if (useray) - m_SmartVolumeMapper->SetRequestedRenderModeToRayCast(); - else - m_SmartVolumeMapper->SetRequestedRenderModeToDefault(); + m_SmartVolumeMapper->SetRequestedRenderModeToGPU(); int blendMode; if (this->GetDataNode()->GetIntProperty("volumerendering.blendmode", blendMode)) + { m_SmartVolumeMapper->SetBlendMode(blendMode); - else if (usemip) - m_SmartVolumeMapper->SetBlendMode(vtkSmartVolumeMapper::MAXIMUM_INTENSITY_BLEND); + } // shading parameter - if (m_SmartVolumeMapper->GetRequestedRenderMode() == vtkSmartVolumeMapper::GPURenderMode) - { - float value = 0; - if (this->GetDataNode()->GetFloatProperty("volumerendering.gpu.ambient", value, renderer)) - m_VolumeProperty->SetAmbient(value); - if (this->GetDataNode()->GetFloatProperty("volumerendering.gpu.diffuse", value, renderer)) - m_VolumeProperty->SetDiffuse(value); - if (this->GetDataNode()->GetFloatProperty("volumerendering.gpu.specular", value, renderer)) - m_VolumeProperty->SetSpecular(value); - if (this->GetDataNode()->GetFloatProperty("volumerendering.gpu.specular.power", value, renderer)) - m_VolumeProperty->SetSpecularPower(value); - } - else - { - float value = 0; - if (this->GetDataNode()->GetFloatProperty("volumerendering.cpu.ambient", value, renderer)) - m_VolumeProperty->SetAmbient(value); - if (this->GetDataNode()->GetFloatProperty("volumerendering.cpu.diffuse", value, renderer)) - m_VolumeProperty->SetDiffuse(value); - if (this->GetDataNode()->GetFloatProperty("volumerendering.cpu.specular", value, renderer)) - m_VolumeProperty->SetSpecular(value); - if (this->GetDataNode()->GetFloatProperty("volumerendering.cpu.specular.power", value, renderer)) - m_VolumeProperty->SetSpecularPower(value); - } + float value = 0; + if (this->GetDataNode()->GetFloatProperty("volumerendering.ambient", value, renderer)) + m_VolumeProperty->SetAmbient(value); + if (this->GetDataNode()->GetFloatProperty("volumerendering.diffuse", value, renderer)) + m_VolumeProperty->SetDiffuse(value); + if (this->GetDataNode()->GetFloatProperty("volumerendering.specular", value, renderer)) + m_VolumeProperty->SetSpecular(value); + if (this->GetDataNode()->GetFloatProperty("volumerendering.specular.power", value, renderer)) + m_VolumeProperty->SetSpecularPower(value); } mitk::VolumeMapperVtkSmart3D::VolumeMapperVtkSmart3D() { m_SmartVolumeMapper = vtkSmartPointer::New(); m_SmartVolumeMapper->SetBlendModeToComposite(); m_ImageChangeInformation = vtkSmartPointer::New(); m_VolumeProperty = vtkSmartPointer::New(); m_Volume = vtkSmartPointer::New(); } mitk::VolumeMapperVtkSmart3D::~VolumeMapperVtkSmart3D() { } diff --git a/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp b/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp deleted file mode 100644 index d2db1547f4..0000000000 --- a/Modules/MapperExt/src/vtkMitkGPUVolumeRayCastMapper.cpp +++ /dev/null @@ -1,619 +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. - -============================================================================*/ -/*========================================================================= - - Program: Visualization Toolkit - Module: $RCSfile: vtkMitkGPUVolumeRayCastMapper.cxx,v $ - - Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen - All rights reserved. - See Copyright.txt or http://www.kitware.com/Copyright.htm for details. - - This software is distributed WITHOUT ANY WARRANTY; without even - the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR - PURPOSE. See the above copyright notice for more information. - -=========================================================================*/ - -#include "vtkMitkGPUVolumeRayCastMapper.h" -#include "vtkCamera.h" -#include "vtkCellData.h" -#include "vtkCommand.h" // for VolumeMapperRender{Start|End|Progress}Event -#include "vtkDataArray.h" -#include "vtkGPUInfo.h" -#include "vtkGPUInfoList.h" -#include "vtkImageData.h" -#include "vtkImageResample.h" -#include "vtkMultiThreader.h" -#include "vtkPointData.h" -#include "vtkRenderWindow.h" -#include "vtkRenderer.h" -#include "vtkRendererCollection.h" -#include "vtkTimerLog.h" -#include "vtkVolume.h" -#include "vtkVolumeProperty.h" -#include - -vtkCxxSetObjectMacro(vtkMitkGPUVolumeRayCastMapper, MaskInput, vtkImageData); -vtkCxxSetObjectMacro(vtkMitkGPUVolumeRayCastMapper, TransformedInput, vtkImageData); - -vtkMitkGPUVolumeRayCastMapper::vtkMitkGPUVolumeRayCastMapper() -{ - this->AutoAdjustSampleDistances = 1; - this->ImageSampleDistance = 1.0; - this->MinimumImageSampleDistance = 1.0; - this->MaximumImageSampleDistance = 10.0; - this->SampleDistance = 1.0; - this->SmallVolumeRender = 0; - this->BigTimeToDraw = 0.0; - this->SmallTimeToDraw = 0.0; - this->FinalColorWindow = 1.0; - this->FinalColorLevel = 0.5; - this->GeneratingCanonicalView = 0; - this->CanonicalViewImageData = nullptr; - - this->MaskInput = nullptr; - this->MaskBlendFactor = 1.0f; - - this->AMRMode = 0; - this->ClippedCroppingRegionPlanes[0] = VTK_DOUBLE_MAX; - this->ClippedCroppingRegionPlanes[1] = VTK_DOUBLE_MIN; - this->ClippedCroppingRegionPlanes[2] = VTK_DOUBLE_MAX; - this->ClippedCroppingRegionPlanes[3] = VTK_DOUBLE_MIN; - this->ClippedCroppingRegionPlanes[4] = VTK_DOUBLE_MAX; - this->ClippedCroppingRegionPlanes[5] = VTK_DOUBLE_MIN; - - this->MaxMemoryInBytes = 0; - vtkGPUInfoList *l = vtkGPUInfoList::New(); - l->Probe(); - if (l->GetNumberOfGPUs() > 0) - { - vtkGPUInfo *info = l->GetGPUInfo(0); - this->MaxMemoryInBytes = info->GetDedicatedVideoMemory(); - if (this->MaxMemoryInBytes == 0) - { - this->MaxMemoryInBytes = info->GetDedicatedSystemMemory(); - } - // we ignore info->GetSharedSystemMemory(); as this is very slow. - } - l->Delete(); - - if (this->MaxMemoryInBytes == 0) // use some default value: 128MB. - { - this->MaxMemoryInBytes = 128 * 1024 * 1024; - } - - this->MaxMemoryFraction = 0.75; - - this->ReportProgress = true; - - this->TransformedInput = nullptr; - this->LastInput = nullptr; -} - -// ---------------------------------------------------------------------------- -vtkMitkGPUVolumeRayCastMapper::~vtkMitkGPUVolumeRayCastMapper() -{ - this->SetMaskInput(nullptr); - this->SetTransformedInput(nullptr); - this->LastInput = nullptr; -} - -// ---------------------------------------------------------------------------- -// The render method that is called from the volume. If this is a canonical -// view render, a specialized version of this method will be called instead. -// Otherwise we will -// - Invoke a start event -// - Start timing -// - Check that everything is OK for rendering -// - Render -// - Stop the timer and record results -// - Invoke an end event -// ---------------------------------------------------------------------------- -void vtkMitkGPUVolumeRayCastMapper::Render(vtkRenderer *ren, vtkVolume *vol) -{ - // Catch renders that are happening due to a canonical view render and - // handle them separately. - if (this->GeneratingCanonicalView) - { - this->CanonicalViewRender(ren, vol); - return; - } - - // Invoke a VolumeMapperRenderStartEvent - this->InvokeEvent(vtkCommand::VolumeMapperRenderStartEvent, nullptr); - - // Start the timer to time the length of this render - vtkTimerLog *timer = vtkTimerLog::New(); - timer->StartTimer(); - - // Make sure everything about this render is OK. - // This is where the input is updated. - if (this->ValidateRender(ren, vol)) - { - // Everything is OK - so go ahead and really do the render - this->GPURender(ren, vol); - } - - // Stop the timer - timer->StopTimer(); - double t = timer->GetElapsedTime(); - - // cout << "Render Timer " << t << " seconds, " << 1.0/t << " frames per second" << endl; - - this->TimeToDraw = t; - timer->Delete(); - - if (vol->GetAllocatedRenderTime() < 1.0) - { - this->SmallTimeToDraw = t; - } - else - { - this->BigTimeToDraw = t; - } - - // Invoke a VolumeMapperRenderEndEvent - this->InvokeEvent(vtkCommand::VolumeMapperRenderEndEvent, nullptr); -} - -// ---------------------------------------------------------------------------- -// Special version for rendering a canonical view - we don't do things like -// invoke start or end events, and we don't capture the render time. -// ---------------------------------------------------------------------------- -void vtkMitkGPUVolumeRayCastMapper::CanonicalViewRender(vtkRenderer *ren, vtkVolume *vol) -{ - // Make sure everything about this render is OK - if (this->ValidateRender(ren, vol)) - { - // Everything is OK - so go ahead and really do the render - this->GPURender(ren, vol); - } -} - -// ---------------------------------------------------------------------------- -// This method us used by the render method to validate everything before -// attempting to render. This method returns 0 if something is not right - -// such as missing input, a null renderer or a null volume, no scalars, etc. -// In some cases it will produce a vtkErrorMacro message, and in others -// (for example, in the case of cropping planes that define a region with -// a volume or 0 or less) it will fail silently. If everything is OK, it will -// return with a value of 1. -// ---------------------------------------------------------------------------- -int vtkMitkGPUVolumeRayCastMapper::ValidateRender(vtkRenderer *ren, vtkVolume *vol) -{ - // Check that we have everything we need to render. - int goodSoFar = 1; - - // Check for a renderer - we MUST have one - if (!ren) - { - goodSoFar = 0; - vtkErrorMacro("Renderer cannot be null."); - } - - // Check for the volume - we MUST have one - if (goodSoFar && !vol) - { - goodSoFar = 0; - vtkErrorMacro("Volume cannot be null."); - } - - // Don't need to check if we have a volume property - // since the volume will create one if we don't. Also - // don't need to check for the scalar opacity function - // or the RGB transfer function since the property will - // create them if they do not yet exist. - - // However we must currently check that the number of - // color channels is 3 - // TODO: lift this restriction - should work with - // gray functions as well. Right now turning off test - // because otherwise 4 component rendering isn't working. - // Will revisit. - if (goodSoFar && vol->GetProperty()->GetColorChannels() != 3) - { - // goodSoFar = 0; - // vtkErrorMacro("Must have a color transfer function."); - } - - // Check the cropping planes. If they are invalid, just silently - // fail. This will happen when an interactive widget is dragged - // such that it defines 0 or negative volume - this can happen - // and should just not render the volume. - // Check the cropping planes - if (goodSoFar && this->Cropping && (this->CroppingRegionPlanes[0] >= this->CroppingRegionPlanes[1] || - this->CroppingRegionPlanes[2] >= this->CroppingRegionPlanes[3] || - this->CroppingRegionPlanes[4] >= this->CroppingRegionPlanes[5])) - { - // No error message here - we want to be silent - goodSoFar = 0; - } - - // Check that we have input data - auto *input = dynamic_cast(this->GetInput()); - - // If we have a timestamp change or data change then create a new clone. - if (input != this->LastInput || input->GetMTime() > this->TransformedInput->GetMTime()) - { - this->LastInput = input; - - vtkImageData *clone; - if (!this->TransformedInput) - { - clone = vtkImageData::New(); - this->SetTransformedInput(clone); - clone->Delete(); - } - else - { - clone = this->TransformedInput; - } - - clone->ShallowCopy(input); - - // @TODO: This is the workaround to deal with GPUVolumeRayCastMapper - // not able to handle extents starting from non zero values. - // There is not a easy fix in the GPU volume ray cast mapper hence - // this fix has been introduced. - - // Get the current extents. - int extents[6], real_extents[6]; - clone->GetExtent(extents); - clone->GetExtent(real_extents); - - // Get the current origin and spacing. - double origin[3], spacing[3]; - clone->GetOrigin(origin); - clone->GetSpacing(spacing); - - for (int cc = 0; cc < 3; cc++) - { - // Transform the origin and the extents. - origin[cc] = origin[cc] + extents[2 * cc] * spacing[cc]; - extents[2 * cc + 1] -= extents[2 * cc]; - extents[2 * cc] -= extents[2 * cc]; - } - - clone->SetOrigin(origin); - clone->SetExtent(extents); - } - - if (goodSoFar && !this->TransformedInput) - { - vtkErrorMacro("Input is nullptr but is required"); - goodSoFar = 0; - } - - // Update the date then make sure we have scalars. Note - // that we must have point or cell scalars because field - // scalars are not supported. - vtkDataArray *scalars = nullptr; - if (goodSoFar) - { - // Here is where we update the input - // this->TransformedInput->UpdateInformation(); //VTK6_TODO - // this->TransformedInput->SetUpdateExtentToWholeExtent(); - // this->TransformedInput->Update(); - - // Now make sure we can find scalars - scalars = this->GetScalars( - this->TransformedInput, this->ScalarMode, this->ArrayAccessMode, this->ArrayId, this->ArrayName, this->CellFlag); - - // We couldn't find scalars - if (!scalars) - { - vtkErrorMacro("No scalars found on input."); - goodSoFar = 0; - } - // Even if we found scalars, if they are field data scalars that isn't good - else if (this->CellFlag == 2) - { - vtkErrorMacro("Only point or cell scalar support - found field scalars instead."); - goodSoFar = 0; - } - } - - // Make sure the scalar type is actually supported. This mappers supports - // almost all standard scalar types. - if (goodSoFar) - { - switch (scalars->GetDataType()) - { - case VTK_CHAR: - vtkErrorMacro(<< "scalar of type VTK_CHAR is not supported " - << "because this type is platform dependent. " - << "Use VTK_SIGNED_CHAR or VTK_UNSIGNED_CHAR instead."); - goodSoFar = 0; - break; - case VTK_BIT: - vtkErrorMacro("scalar of type VTK_BIT is not supported by this mapper."); - goodSoFar = 0; - break; - case VTK_ID_TYPE: - vtkErrorMacro("scalar of type VTK_ID_TYPE is not supported by this mapper."); - goodSoFar = 0; - break; - case VTK_STRING: - vtkErrorMacro("scalar of type VTK_STRING is not supported by this mapper."); - goodSoFar = 0; - break; - default: - // Don't need to do anything here - break; - } - } - - // Check on the blending type - we support composite and min / max intensity - if (goodSoFar) - { - if (this->BlendMode != vtkVolumeMapper::COMPOSITE_BLEND && - this->BlendMode != vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND && - this->BlendMode != vtkVolumeMapper::MINIMUM_INTENSITY_BLEND) - { - goodSoFar = 0; - vtkErrorMacro(<< "Selected blend mode not supported. " - << "Only Composite and MIP and MinIP modes " - << "are supported by the current implementation."); - } - } - - // This mapper supports 1 component data, or 4 component if it is not independent - // component (i.e. the four components define RGBA) - int numberOfComponents = 0; - if (goodSoFar) - { - numberOfComponents = scalars->GetNumberOfComponents(); - if (!(numberOfComponents == 1 || (numberOfComponents == 4 && vol->GetProperty()->GetIndependentComponents() == 0))) - { - goodSoFar = 0; - vtkErrorMacro(<< "Only one component scalars, or four " - << "component with non-independent components, " - << "are supported by this mapper."); - } - } - - // If this is four component data, then it better be unsigned char (RGBA). - if (goodSoFar && numberOfComponents == 4 && scalars->GetDataType() != VTK_UNSIGNED_CHAR) - { - goodSoFar = 0; - vtkErrorMacro("Only unsigned char is supported for 4-component scalars!"); - } - - // return our status - return goodSoFar; -} - -// ---------------------------------------------------------------------------- -// Description: -// Called by the AMR Volume Mapper. -// Set the flag that tells if the scalars are on point data (0) or -// cell data (1). -void vtkMitkGPUVolumeRayCastMapper::SetCellFlag(int cellFlag) -{ - this->CellFlag = cellFlag; -} - -// ---------------------------------------------------------------------------- -void vtkMitkGPUVolumeRayCastMapper::CreateCanonicalView(vtkRenderer *ren, - vtkVolume *volume, - vtkImageData *image, - int vtkNotUsed(blend_mode), - double viewDirection[3], - double viewUp[3]) -{ - this->GeneratingCanonicalView = 1; - int oldSwap = ren->GetRenderWindow()->GetSwapBuffers(); - ren->GetRenderWindow()->SwapBuffersOff(); - - int dim[3]; - image->GetDimensions(dim); - int *size = ren->GetRenderWindow()->GetSize(); - - vtkImageData *bigImage = vtkImageData::New(); - bigImage->SetDimensions(size[0], size[1], 1); - bigImage->AllocateScalars(VTK_UNSIGNED_CHAR, 3); - - this->CanonicalViewImageData = bigImage; - - double scale[2]; - scale[0] = dim[0] / static_cast(size[0]); - scale[1] = dim[1] / static_cast(size[1]); - - // Save the visibility flags of the renderers and set all to false except - // for the ren. - vtkRendererCollection *renderers = ren->GetRenderWindow()->GetRenderers(); - int numberOfRenderers = renderers->GetNumberOfItems(); - - auto rendererVisibilities = new bool[numberOfRenderers]; - renderers->InitTraversal(); - int i = 0; - while (i < numberOfRenderers) - { - vtkRenderer *r = renderers->GetNextItem(); - rendererVisibilities[i] = r->GetDraw() == 1; - if (r != ren) - { - r->SetDraw(false); - } - ++i; - } - - // Save the visibility flags of the props and set all to false except - // for the volume. - - vtkPropCollection *props = ren->GetViewProps(); - int numberOfProps = props->GetNumberOfItems(); - - auto propVisibilities = new bool[numberOfProps]; - props->InitTraversal(); - i = 0; - while (i < numberOfProps) - { - vtkProp *p = props->GetNextProp(); - propVisibilities[i] = p->GetVisibility() == 1; - if (p != volume) - { - p->SetVisibility(false); - } - ++i; - } - - vtkCamera *savedCamera = ren->GetActiveCamera(); - savedCamera->Modified(); - vtkCamera *canonicalViewCamera = vtkCamera::New(); - - // Code from vtkFixedPointVolumeRayCastMapper: - double *center = volume->GetCenter(); - double bounds[6]; - volume->GetBounds(bounds); - double d = - sqrt((bounds[1] - bounds[0]) * (bounds[1] - bounds[0]) + (bounds[3] - bounds[2]) * (bounds[3] - bounds[2]) + - (bounds[5] - bounds[4]) * (bounds[5] - bounds[4])); - - // For now use x distance - need to change this - d = bounds[1] - bounds[0]; - - // Set up the camera in parallel - canonicalViewCamera->SetFocalPoint(center); - canonicalViewCamera->ParallelProjectionOn(); - canonicalViewCamera->SetPosition( - center[0] - d * viewDirection[0], center[1] - d * viewDirection[1], center[2] - d * viewDirection[2]); - canonicalViewCamera->SetViewUp(viewUp); - canonicalViewCamera->SetParallelScale(d / 2); - - ren->SetActiveCamera(canonicalViewCamera); - ren->GetRenderWindow()->Render(); - - ren->SetActiveCamera(savedCamera); - canonicalViewCamera->Delete(); - - // Shrink to image to the desired size - vtkImageResample *resample = vtkImageResample::New(); - resample->SetInputData(bigImage); - resample->SetAxisMagnificationFactor(0, scale[0]); - resample->SetAxisMagnificationFactor(1, scale[1]); - resample->SetAxisMagnificationFactor(2, 1); - resample->UpdateWholeExtent(); - - // Copy the pixels over - image->DeepCopy(resample->GetOutput()); - - bigImage->Delete(); - resample->Delete(); - - // Restore the visibility flags of the props - props->InitTraversal(); - i = 0; - while (i < numberOfProps) - { - vtkProp *p = props->GetNextProp(); - p->SetVisibility(propVisibilities[i]); - ++i; - } - - delete[] propVisibilities; - - // Restore the visibility flags of the renderers - renderers->InitTraversal(); - i = 0; - while (i < numberOfRenderers) - { - vtkRenderer *r = renderers->GetNextItem(); - r->SetDraw(rendererVisibilities[i]); - ++i; - } - - delete[] rendererVisibilities; - - ren->GetRenderWindow()->SetSwapBuffers(oldSwap); - this->CanonicalViewImageData = nullptr; - this->GeneratingCanonicalView = 0; -} - -// ---------------------------------------------------------------------------- -// Print method for vtkMitkGPUVolumeRayCastMapper -void vtkMitkGPUVolumeRayCastMapper::PrintSelf(ostream &os, vtkIndent indent) -{ - this->Superclass::PrintSelf(os, indent); - - os << indent << "AutoAdjustSampleDistances: " << this->AutoAdjustSampleDistances << endl; - os << indent << "MinimumImageSampleDistance: " << this->MinimumImageSampleDistance << endl; - os << indent << "MaximumImageSampleDistance: " << this->MaximumImageSampleDistance << endl; - os << indent << "ImageSampleDistance: " << this->ImageSampleDistance << endl; - os << indent << "SampleDistance: " << this->SampleDistance << endl; - os << indent << "FinalColorWindow: " << this->FinalColorWindow << endl; - os << indent << "FinalColorLevel: " << this->FinalColorLevel << endl; - os << indent << "MaskInput: " << this->MaskInput << endl; - os << indent << "MaskBlendFactor: " << this->MaskBlendFactor << endl; - os << indent << "MaxMemoryInBytes: " << this->MaxMemoryInBytes << endl; - os << indent << "MaxMemoryFraction: " << this->MaxMemoryFraction << endl; - os << indent << "ReportProgress: " << this->ReportProgress << endl; -} - -// ---------------------------------------------------------------------------- -// Description: -// Compute the cropping planes clipped by the bounds of the volume. -// The result is put into this->ClippedCroppingRegionPlanes. -// NOTE: IT WILL BE MOVED UP TO vtkVolumeMapper after bullet proof usage -// in this mapper. Other subclasses will use the ClippedCroppingRegionsPlanes -// members instead of CroppingRegionPlanes. -// \pre volume_exists: this->GetInput()!=0 -// \pre valid_cropping: this->Cropping && -// this->CroppingRegionPlanes[0]CroppingRegionPlanes[1] && -// this->CroppingRegionPlanes[2]CroppingRegionPlanes[3] && -// this->CroppingRegionPlanes[4]CroppingRegionPlanes[5]) -void vtkMitkGPUVolumeRayCastMapper::ClipCroppingRegionPlanes() -{ - assert("pre: volume_exists" && this->GetInput() != nullptr); - assert("pre: valid_cropping" && this->Cropping && this->CroppingRegionPlanes[0] < this->CroppingRegionPlanes[1] && - this->CroppingRegionPlanes[2] < this->CroppingRegionPlanes[3] && - this->CroppingRegionPlanes[4] < this->CroppingRegionPlanes[5]); - - // vtkVolumeMapper::Render() will have something like: - // if(this->Cropping && (this->CroppingRegionPlanes[0]>=this->CroppingRegionPlanes[1] || - // this->CroppingRegionPlanes[2]>=this->CroppingRegionPlanes[3] || - // this->CroppingRegionPlanes[4]>=this->CroppingRegionPlanes[5])) - // { - // // silentely stop because the cropping is not valid. - // return; - // } - - double volBounds[6]; - this->GetInput()->GetBounds(volBounds); - - int i = 0; - while (i < 6) - { - // max of the mins - if (this->CroppingRegionPlanes[i] < volBounds[i]) - { - this->ClippedCroppingRegionPlanes[i] = volBounds[i]; - } - else - { - this->ClippedCroppingRegionPlanes[i] = this->CroppingRegionPlanes[i]; - } - ++i; - // min of the maxs - if (this->CroppingRegionPlanes[i] > volBounds[i]) - { - this->ClippedCroppingRegionPlanes[i] = volBounds[i]; - } - - else - { - this->ClippedCroppingRegionPlanes[i] = this->CroppingRegionPlanes[i]; - } - ++i; - } -} diff --git a/Modules/MapperExt/src/vtkMitkOpenGLVolumeTextureMapper3D.cpp b/Modules/MapperExt/src/vtkMitkOpenGLVolumeTextureMapper3D.cpp deleted file mode 100644 index aaa400fbbf..0000000000 --- a/Modules/MapperExt/src/vtkMitkOpenGLVolumeTextureMapper3D.cpp +++ /dev/null @@ -1,2443 +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. - -============================================================================*/ - -#ifdef _OPENMP -#include -#endif - -#include "vtkWindows.h" -#include "mitkCommon.h" -#include "vtkMitkOpenGLVolumeTextureMapper3D.h" - -#define GPU_INFO MITK_INFO("mapper.vr") -#define GPU_WARN MITK_WARN("mapper.vr") - -#include "vtkCamera.h" -#include "vtkDataArray.h" -#include "vtkImageData.h" -#include "vtkLight.h" -#include "vtkLightCollection.h" -#include "vtkMath.h" -#include "vtkMatrix4x4.h" -#include "vtkObjectFactory.h" -#include "vtkOpenGLExtensionManager.h" -#include "vtkPlane.h" -#include "vtkPlaneCollection.h" -#include "vtkPointData.h" -#include "vtkRenderWindow.h" -#include "vtkRenderer.h" -#include "vtkTimerLog.h" -#include "vtkTransform.h" -#include "vtkVolumeProperty.h" -#include "vtkgl.h" - -#include "vtkOpenGLRenderWindow.h" - -#define myGL_COMPRESSED_RGB_S3TC_DXT1_EXT 0x83F0 -#define myGL_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT 0x8C72 -#define myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT 0x83F3 - -const char *vtkMitkVolumeTextureMapper3D_FourDependentShadeFP = "!!ARBfp1.0\n" - - //# We need some temporary variables - "TEMP index, normal, finalColor;\n" - "TEMP temp,temp1, temp2, temp3,temp4; \n" - "TEMP sampleColor;\n" - "TEMP ndotl, ndoth, ndotv; \n" - "TEMP lightInfo, lightResult;\n" - - //# We are going to use the first - //# texture coordinate - "ATTRIB tex0 = fragment.texcoord[0];\n" - - //# This is the lighting information - "PARAM lightDirection = program.local[0];\n" - "PARAM halfwayVector = program.local[1];\n" - "PARAM coefficient = program.local[2];\n" - "PARAM lightDiffColor = program.local[3]; \n" - "PARAM lightSpecColor = program.local[4]; \n" - "PARAM viewVector = program.local[5];\n" - "PARAM constants = program.local[6];\n" - - //# This is our output color - "OUTPUT out = result.color;\n" - - //# Look up the gradient direction - //# in the third volume - "TEX temp2, tex0, texture[0], 3D;\n" - - //# This normal is stored 0 to 1, change to -1 to 1 - //# by multiplying by 2.0 then adding -1.0. - "MAD normal, temp2, constants.x, constants.y;\n" - - "DP3 temp4, normal, normal;\n" - "RSQ temp, temp4.x;\n" - "MUL normal, normal, temp;\n" - - //"RCP temp4,temp.x;\n" - - //"MUL temp2.w,temp2.w,temp4.x;\n" - - //"MUL_SAT temp2.w,temp2.w,6.0;\n" - - "TEX sampleColor, tex0, texture[1], 3D;\n" - - //# Take the dot product of the light - //# direction and the normal - "DP3 ndotl, normal, lightDirection;\n" - - //# Take the dot product of the halfway - //# vector and the normal - "DP3 ndoth, normal, halfwayVector;\n" - - "DP3 ndotv, normal, viewVector;\n" - - //# flip if necessary for two sided lighting - "MUL temp3, ndotl, constants.y; \n" - "CMP ndotl, ndotv, ndotl, temp3;\n" - "MUL temp3, ndoth, constants.y; \n" - "CMP ndoth, ndotv, ndoth, temp3;\n" - - //# put the pieces together for a LIT operation - "MOV lightInfo.x, ndotl.x; \n" - "MOV lightInfo.y, ndoth.x; \n" - "MOV lightInfo.w, coefficient.w; \n" - - //# compute the lighting - "LIT lightResult, lightInfo;\n" - - //# COLOR FIX - "MUL lightResult, lightResult, 4.0;\n" - - //# This is the ambient contribution - "MUL finalColor, coefficient.x, sampleColor;\n" - - //# This is the diffuse contribution - "MUL temp3, lightDiffColor, sampleColor;\n" - "MUL temp3, temp3, lightResult.y;\n" - "ADD finalColor, finalColor, temp3;\n" - - //# This is th specular contribution - "MUL temp3, lightSpecColor, lightResult.z; \n" - - //# Add specular into result so far, and replace - //# with the original alpha. - "ADD out, finalColor, temp3;\n" - "MOV out.w, temp2.w;\n" - - "END\n"; - -const char *vtkMitkVolumeTextureMapper3D_OneComponentShadeFP = "!!ARBfp1.0\n" - - //# This is the fragment program for one - //# component data with shading - - //# We need some temporary variables - "TEMP index, normal, finalColor;\n" - "TEMP temp,temp1, temp2, temp3,temp4; \n" - "TEMP sampleColor;\n" - "TEMP ndotl, ndoth, ndotv; \n" - "TEMP lightInfo, lightResult;\n" - - //# We are going to use the first - //# texture coordinate - "ATTRIB tex0 = fragment.texcoord[0];\n" - - //# This is the lighting information - "PARAM lightDirection = program.local[0];\n" - "PARAM halfwayVector = program.local[1];\n" - "PARAM coefficient = program.local[2];\n" - "PARAM lightDiffColor = program.local[3]; \n" - "PARAM lightSpecColor = program.local[4]; \n" - "PARAM viewVector = program.local[5];\n" - "PARAM constants = program.local[6];\n" - - //# This is our output color - "OUTPUT out = result.color;\n" - - //# Look up the gradient direction - //# in the third volume - "TEX temp2, tex0, texture[0], 3D;\n" - - // Gradient Compution - - //# Look up the scalar value / gradient - //# magnitude in the first volume - //"TEX temp1, tex0, texture[0], 3D;\n" - - /* - - "ADD temp3,tex0,{-0.005,0,0};\n" - "TEX temp2,temp3, texture[0], 3D;\n" - - //"ADD temp3,tex0,{ 0.005,0,0};\n" - //"TEX temp1,temp3, texture[0], 3D;\n" - - "SUB normal.x,temp2.y,temp1.y;\n" - - "ADD temp3,tex0,{0,-0.005,0};\n" - "TEX temp2,temp3, texture[0], 3D;\n" - - //"ADD temp3,tex0,{0, 0.005,0};\n" - //"TEX temp1,temp3, texture[0], 3D;\n" - - "SUB normal.y,temp2.y,temp1.y;\n" - - "ADD temp3,tex0,{0,0,-0.005};\n" - "TEX temp2,temp3, texture[0], 3D;\n" - - //"ADD temp3,tex0,{0,0, 0.005};\n" - //"TEX temp1,temp3, texture[0], 3D;\n" - - "SUB normal.z,temp2.y,temp1.y;\n" - - */ - - //"MOV normal,{1,1,1};\n" - - "MOV index.x,temp2.a;\n" - - //# This normal is stored 0 to 1, change to -1 to 1 - //# by multiplying by 2.0 then adding -1.0. - "MAD normal, temp2, constants.x, constants.y;\n" - - //# Swizzle this to use (a,r) as texture - //# coordinates - //"SWZ index, temp1, a, r, 1, 1;\n" - - //# Use this coordinate to look up a - //# final color in the third texture - //# (this is a 2D texture) - - "DP3 temp4, normal, normal;\n" - - "RSQ temp, temp4.x;\n" - - "RCP temp4,temp.x;\n" - - "MUL normal, normal, temp;\n" - - "MOV index.y, temp4.x;\n" - - "TEX sampleColor, index, texture[1], 2D;\n" - - //"MUL sampleColor.w,sampleColor.w,temp4.x;\n" - - //# Take the dot product of the light - //# direction and the normal - "DP3 ndotl, normal, lightDirection;\n" - - //# Take the dot product of the halfway - //# vector and the normal - "DP3 ndoth, normal, halfwayVector;\n" - - "DP3 ndotv, normal, viewVector;\n" - - //# flip if necessary for two sided lighting - "MUL temp3, ndotl, constants.y; \n" - "CMP ndotl, ndotv, ndotl, temp3;\n" - "MUL temp3, ndoth, constants.y; \n" - "CMP ndoth, ndotv, ndoth, temp3;\n" - - //# put the pieces together for a LIT operation - "MOV lightInfo.x, ndotl.x; \n" - "MOV lightInfo.y, ndoth.x; \n" - "MOV lightInfo.w, coefficient.w; \n" - - //# compute the lighting - "LIT lightResult, lightInfo;\n" - - //# COLOR FIX - "MUL lightResult, lightResult, 4.0;\n" - - //# This is the ambient contribution - "MUL finalColor, coefficient.x, sampleColor;\n" - - //# This is the diffuse contribution - "MUL temp3, lightDiffColor, sampleColor;\n" - "MUL temp3, temp3, lightResult.y;\n" - "ADD finalColor, finalColor, temp3;\n" - - //# This is th specular contribution - "MUL temp3, lightSpecColor, lightResult.z; \n" - - //# Add specular into result so far, and replace - //# with the original alpha. - "ADD out, finalColor, temp3;\n" - "MOV out.w, sampleColor.w;\n" - - "END\n"; - -//#ifndef VTK_IMPLEMENT_MESA_CXX -vtkStandardNewMacro(vtkMitkOpenGLVolumeTextureMapper3D); -//#endif - -vtkMitkOpenGLVolumeTextureMapper3D::vtkMitkOpenGLVolumeTextureMapper3D() -{ - // GPU_INFO << "vtkMitkOpenGLVolumeTextureMapper3D"; - - this->Initialized = 0; - this->Volume1Index = 0; - this->Volume2Index = 0; - this->Volume3Index = 0; - this->ColorLookupIndex = 0; - this->AlphaLookupIndex = 0; - this->RenderWindow = nullptr; - this->SupportsCompressedTexture = false; - - prgOneComponentShade = 0; - prgRGBAShade = 0; -} - -vtkMitkOpenGLVolumeTextureMapper3D::~vtkMitkOpenGLVolumeTextureMapper3D() -{ - // GPU_INFO << "~vtkMitkOpenGLVolumeTextureMapper3D"; - if (prgOneComponentShade) - vtkgl::DeleteProgramsARB(1, &prgOneComponentShade); - - if (prgRGBAShade) - vtkgl::DeleteProgramsARB(1, &prgRGBAShade); -} - -// Release the graphics resources used by this texture. -void vtkMitkOpenGLVolumeTextureMapper3D::ReleaseGraphicsResources(vtkWindow *renWin) -{ - // GPU_INFO << "ReleaseGraphicsResources"; - - if ((this->Volume1Index || this->Volume2Index || this->Volume3Index || this->ColorLookupIndex) && renWin) - { - static_cast(renWin)->MakeCurrent(); -#ifdef GL_VERSION_1_1 - // free any textures - this->DeleteTextureIndex(&this->Volume1Index); - this->DeleteTextureIndex(&this->Volume2Index); - this->DeleteTextureIndex(&this->Volume3Index); - this->DeleteTextureIndex(&this->ColorLookupIndex); - this->DeleteTextureIndex(&this->AlphaLookupIndex); -#endif - } - this->Volume1Index = 0; - this->Volume2Index = 0; - this->Volume3Index = 0; - this->ColorLookupIndex = 0; - this->RenderWindow = nullptr; - this->SupportsCompressedTexture = false; - this->SupportsNonPowerOfTwoTextures = false; - - this->Modified(); -} - -// Release the graphics resources used by this texture. -void vtkMitkOpenGLVolumeTextureMapper3D::ReleaseGraphicsResources(mitk::BaseRenderer *renderer) -{ - // GPU_INFO << "ReleaseGraphicsResources"; - - vtkWindow *renWin = renderer->GetVtkRenderer()->GetRenderWindow(); - - if ((this->Volume1Index || this->Volume2Index || this->Volume3Index || this->ColorLookupIndex) && renWin) - { - static_cast(renWin)->MakeCurrent(); -#ifdef GL_VERSION_1_1 - // free any textures - this->DeleteTextureIndex(&this->Volume1Index); - this->DeleteTextureIndex(&this->Volume2Index); - this->DeleteTextureIndex(&this->Volume3Index); - this->DeleteTextureIndex(&this->ColorLookupIndex); - this->DeleteTextureIndex(&this->AlphaLookupIndex); -#endif - } - this->Volume1Index = 0; - this->Volume2Index = 0; - this->Volume3Index = 0; - this->ColorLookupIndex = 0; - this->RenderWindow = nullptr; - this->SupportsCompressedTexture = false; - this->SupportsNonPowerOfTwoTextures = false; - - this->Modified(); -} - -void vtkMitkOpenGLVolumeTextureMapper3D::Render(vtkRenderer *ren, vtkVolume *vol) -{ - // GPU_INFO << "Render"; - - ren->GetRenderWindow()->MakeCurrent(); - - if (!this->Initialized) - { - // this->Initialize(); - this->Initialize(ren); - } - - if (!this->RenderPossible) - { - vtkErrorMacro("required extensions not supported"); - return; - } - - vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); - vtkPlaneCollection *clipPlanes; - vtkPlane *plane; - int numClipPlanes = 0; - double planeEquation[4]; - - // build transformation - vol->GetMatrix(matrix); - matrix->Transpose(); - - glPushAttrib(GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_POLYGON_BIT | - GL_TEXTURE_BIT); - - int i; - - // Use the OpenGL clip planes - clipPlanes = this->ClippingPlanes; - if (clipPlanes) - { - numClipPlanes = clipPlanes->GetNumberOfItems(); - if (numClipPlanes > 6) - { - vtkErrorMacro(<< "OpenGL guarantees only 6 additional clipping planes"); - } - - for (i = 0; i < numClipPlanes; i++) - { - glEnable(static_cast(GL_CLIP_PLANE0 + i)); - - plane = static_cast(clipPlanes->GetItemAsObject(i)); - - planeEquation[0] = plane->GetNormal()[0]; - planeEquation[1] = plane->GetNormal()[1]; - planeEquation[2] = plane->GetNormal()[2]; - planeEquation[3] = -(planeEquation[0] * plane->GetOrigin()[0] + planeEquation[1] * plane->GetOrigin()[1] + - planeEquation[2] * plane->GetOrigin()[2]); - glClipPlane(static_cast(GL_CLIP_PLANE0 + i), planeEquation); - } - } - - // insert model transformation - glMatrixMode(GL_MODELVIEW); - glPushMatrix(); - glMultMatrixd(matrix->Element[0]); - - glColor4f(1.0, 1.0, 1.0, 1.0); - - // Turn lighting off - the polygon textures already have illumination - glDisable(GL_LIGHTING); - - // vtkGraphicErrorMacro(ren->GetRenderWindow(),"Before actual render method"); - - this->RenderFP(ren, vol); - - // pop transformation matrix - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - - matrix->Delete(); - glPopAttrib(); -} - -void vtkMitkOpenGLVolumeTextureMapper3D::RenderFP(vtkRenderer *ren, vtkVolume *vol) -{ - // GPU_INFO << "RenderFP"; - - /* - glAlphaFunc (GL_GREATER, static_cast(1.0/255.0)); - glEnable (GL_ALPHA_TEST); - */ - - glEnable(GL_BLEND); - glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - int components = this->GetInput()->GetNumberOfScalarComponents(); - switch (components) - { - case 1: - this->RenderOneIndependentShadeFP(ren, vol); - break; - - case 4: - this->RenderRGBAShadeFP(ren, vol); - break; - } - - vtkgl::ActiveTexture(vtkgl::TEXTURE2); - glDisable(GL_TEXTURE_2D); - glDisable(vtkgl::TEXTURE_3D); - - vtkgl::ActiveTexture(vtkgl::TEXTURE1); - glDisable(GL_TEXTURE_2D); - glDisable(vtkgl::TEXTURE_3D); - - vtkgl::ActiveTexture(vtkgl::TEXTURE0); - glDisable(GL_TEXTURE_2D); - glDisable(vtkgl::TEXTURE_3D); - - glDisable(GL_BLEND); -} - -void vtkMitkOpenGLVolumeTextureMapper3D::DeleteTextureIndex(GLuint *index) -{ - // GPU_INFO << "DeleteTextureIndex"; - - if (glIsTexture(*index)) - { - GLuint tempIndex; - tempIndex = *index; - glDeleteTextures(1, &tempIndex); - *index = 0; - } -} - -void vtkMitkOpenGLVolumeTextureMapper3D::CreateTextureIndex(GLuint *index) -{ - // GPU_INFO << "CreateTextureIndex"; - - GLuint tempIndex = 0; - glGenTextures(1, &tempIndex); - *index = static_cast(tempIndex); -} - -void vtkMitkOpenGLVolumeTextureMapper3D::RenderPolygons(vtkRenderer *ren, vtkVolume *vol, int stages[4]) -{ - // GPU_INFO << "RenderPolygons"; - - vtkRenderWindow *renWin = ren->GetRenderWindow(); - - if (renWin->CheckAbortStatus()) - { - return; - } - - double bounds[27][6]; - float distance2[27]; - - int numIterations; - int i, j, k; - - // No cropping case - render the whole thing - if (!this->Cropping) - { - // Use the input data bounds - we'll take care of the volume's - // matrix during rendering - this->GetInput()->GetBounds(bounds[0]); - numIterations = 1; - } - // Simple cropping case - render the subvolume - else if (this->CroppingRegionFlags == 0x2000) - { - this->GetCroppingRegionPlanes(bounds[0]); - numIterations = 1; - } - // Complex cropping case - render each region in back-to-front order - else - { - // Get the camera position - double camPos[4]; - ren->GetActiveCamera()->GetPosition(camPos); - - double volBounds[6]; - this->GetInput()->GetBounds(volBounds); - - // Pass camera through inverse volume matrix - // so that we are in the same coordinate system - vtkMatrix4x4 *volMatrix = vtkMatrix4x4::New(); - vol->GetMatrix(volMatrix); - camPos[3] = 1.0; - volMatrix->Invert(); - volMatrix->MultiplyPoint(camPos, camPos); - volMatrix->Delete(); - if (camPos[3]) - { - camPos[0] /= camPos[3]; - camPos[1] /= camPos[3]; - camPos[2] /= camPos[3]; - } - - // These are the region limits for x (first four), y (next four) and - // z (last four). The first region limit is the lower bound for - // that axis, the next two are the region planes along that axis, and - // the final one in the upper bound for that axis. - float limit[12]; - for (i = 0; i < 3; i++) - { - limit[i * 4] = volBounds[i * 2]; - limit[i * 4 + 1] = this->CroppingRegionPlanes[i * 2]; - limit[i * 4 + 2] = this->CroppingRegionPlanes[i * 2 + 1]; - limit[i * 4 + 3] = volBounds[i * 2 + 1]; - } - - // For each of the 27 possible regions, find out if it is enabled, - // and if so, compute the bounds and the distance from the camera - // to the center of the region. - int numRegions = 0; - int region; - for (region = 0; region < 27; region++) - { - int regionFlag = 1 << region; - - if (this->CroppingRegionFlags & regionFlag) - { - // what is the coordinate in the 3x3x3 grid - int loc[3]; - loc[0] = region % 3; - loc[1] = (region / 3) % 3; - loc[2] = (region / 9) % 3; - - // compute the bounds and center - float center[3]; - for (i = 0; i < 3; i++) - { - bounds[numRegions][i * 2] = limit[4 * i + loc[i]]; - bounds[numRegions][i * 2 + 1] = limit[4 * i + loc[i] + 1]; - center[i] = (bounds[numRegions][i * 2] + bounds[numRegions][i * 2 + 1]) / 2.0; - } - - // compute the distance squared to the center - distance2[numRegions] = (camPos[0] - center[0]) * (camPos[0] - center[0]) + - (camPos[1] - center[1]) * (camPos[1] - center[1]) + - (camPos[2] - center[2]) * (camPos[2] - center[2]); - - // we've added one region - numRegions++; - } - } - - // Do a quick bubble sort on distance - for (i = 1; i < numRegions; i++) - { - for (j = i; j > 0 && distance2[j] > distance2[j - 1]; j--) - { - float tmpBounds[6]; - float tmpDistance2; - - for (k = 0; k < 6; k++) - { - tmpBounds[k] = bounds[j][k]; - } - tmpDistance2 = distance2[j]; - - for (k = 0; k < 6; k++) - { - bounds[j][k] = bounds[j - 1][k]; - } - distance2[j] = distance2[j - 1]; - - for (k = 0; k < 6; k++) - { - bounds[j - 1][k] = tmpBounds[k]; - } - distance2[j - 1] = tmpDistance2; - } - } - - numIterations = numRegions; - } - - // loop over all regions we need to render - for (int loop = 0; loop < numIterations; loop++) - { - // Compute the set of polygons for this region - // according to the bounds - this->ComputePolygons(ren, vol, bounds[loop]); - - // Loop over the polygons - for (i = 0; i < this->NumberOfPolygons; i++) - { - if (renWin->CheckAbortStatus()) - { - return; - } - - float *ptr = this->PolygonBuffer + 36 * i; - - glBegin(GL_TRIANGLE_FAN); - - for (j = 0; j < 6; j++) - { - if (ptr[0] < 0.0) - { - break; - } - - for (k = 0; k < 4; k++) - { - if (stages[k]) - { - vtkgl::MultiTexCoord3fv(vtkgl::TEXTURE0 + k, ptr); - } - } - glVertex3fv(ptr + 3); - - ptr += 6; - } - glEnd(); - } - } -} - -// This method moves the scalars from the input volume into volume1 (and -// possibly volume2) which are the 3D texture maps used for rendering. -// -// In the case where our volume is a power of two, the copy is done -// directly. If we need to resample, then trilinear interpolation is used. -// -// A shift/scale is applied to the input scalar value to produce an 8 bit -// value for the texture volume. -// -// When the input data is one component, the scalar value is placed in the -// second component of the two component volume1. The first component is -// filled in later with the gradient magnitude. -// -// When the input data is two component non-independent, the first component -// of the input data is placed in the first component of volume1, and the -// second component of the input data is placed in the third component of -// volume1. Volume1 has three components - the second is filled in later with -// the gradient magnitude. -// -// When the input data is four component non-independent, the first three -// components of the input data are placed in volume1 (which has three -// components), and the fourth component is placed in the second component -// of volume2. The first component of volume2 is later filled in with the -// gradient magnitude. - -template -class ScalarGradientCompute -{ - T *dataPtr; - unsigned char *tmpPtr; - unsigned char *tmpPtr2; - int sizeX; - int sizeY; - int sizeZ; - int sizeXY; - int sizeXm1; - int sizeYm1; - int sizeZm1; - int fullX; - int fullY; - int fullZ; - int fullXY; - int currentChunkStart; - int currentChunkEnd; - - int offZ; - - float offset; - float scale; - -public: - ScalarGradientCompute(T *_dataPtr, - unsigned char *_tmpPtr, - unsigned char *_tmpPtr2, - int _sizeX, - int _sizeY, - int _sizeZ, - int _fullX, - int _fullY, - int _fullZ, - float _offset, - float _scale) - { - dataPtr = _dataPtr; - tmpPtr = _tmpPtr; - tmpPtr2 = _tmpPtr2; - sizeX = _sizeX; - sizeY = _sizeY; - sizeZ = _sizeZ; - fullX = _fullX; - fullY = _fullY; - fullZ = _fullZ; - offset = _offset; - scale = _scale; - - sizeXY = sizeX * sizeY; - sizeXm1 = sizeX - 1; - sizeYm1 = sizeY - 1; - sizeZm1 = sizeZ - 1; - - fullXY = fullX * fullY; - currentChunkStart = 0; - currentChunkEnd = 0; - offZ = 0; - } - - inline float sample(int x, int y, int z) { return float(dataPtr[x + y * sizeX + z * sizeXY]); } - inline void fill(int x, int y, int z) - { - int doff = x + y * fullX + (z - offZ) * fullXY; - - tmpPtr[doff * 4 + 0] = 0; - tmpPtr[doff * 4 + 1] = 0; - tmpPtr[doff * 4 + 2] = 0; - tmpPtr[doff * 4 + 3] = 0; - /* -tmpPtr2[doff*3+0]= 0; -tmpPtr2[doff*3+1]= 0; -tmpPtr2[doff*3+2]= 0; -*/ - } - - inline int clamp(int x) - { - if (x < 0) - x = 0; - else if (x > 255) - x = 255; - return x; - } - - inline void write(int x, int y, int z, float grayValue, float gx, float gy, float gz) - { - /* - gx /= aspect[0]; - gy /= aspect[1]; - gz /= aspect[2]; - */ - // Compute the gradient magnitude - - int iGrayValue = static_cast((grayValue + offset) * scale + 0.5f); - - gx *= scale; - gy *= scale; - gz *= scale; - - float t = sqrtf(gx * gx + gy * gy + gz * gz); - - if (t > 0.01f) - { - if (t < 2.0f) - { - float fac = 2.0f / t; - gx *= fac; - gy *= fac; - gz *= fac; - } - else if (t > 255.0f) - { - float fac = 255.0f / t; - gx *= fac; - gy *= fac; - gz *= fac; - } - } - else - { - gx = gy = gz = 0.0f; - } - - int nx = static_cast(0.5f * gx + 127.5f); - int ny = static_cast(0.5f * gy + 127.5f); - int nz = static_cast(0.5f * gz + 127.5f); - - int doff = x + y * fullX + (z - offZ) * fullXY; - - // tmpPtr[doff*2+0]= 0; - - tmpPtr[doff * 4 + 0] = clamp(nx); - tmpPtr[doff * 4 + 1] = clamp(ny); - tmpPtr[doff * 4 + 2] = clamp(nz); - tmpPtr[doff * 4 + 3] = clamp(iGrayValue); - - /* - if( z == fullZ/2 ) - if( y == fullY/2 ) - MITK_INFO << x << " " << y << " " << z << " : " << iGrayValue << " : " << iGradient; - */ - } - - inline void compute(int x, int y, int z) - { - float grayValue = sample(x, y, z); - float gx, gy, gz; - - gx = sample(x + 1, y, z) - sample(x - 1, y, z); - gy = sample(x, y + 1, z) - sample(x, y - 1, z); - gz = sample(x, y, z + 1) - sample(x, y, z - 1); - - write(x, y, z, grayValue, gx, gy, gz); - } - - inline void computeClamp(int x, int y, int z) - { - float grayValue = sample(x, y, z); - float gx, gy, gz; - - if (x == 0) - gx = 2.0f * (sample(x + 1, y, z) - grayValue); - else if (x == sizeXm1) - gx = 2.0f * (grayValue - sample(x - 1, y, z)); - else - gx = sample(x + 1, y, z) - sample(x - 1, y, z); - - if (y == 0) - gy = 2.0f * (sample(x, y + 1, z) - grayValue); - else if (y == sizeYm1) - gy = 2.0f * (grayValue - sample(x, y - 1, z)); - else - gy = sample(x, y + 1, z) - sample(x, y - 1, z); - - if (z == 0) - gz = 2.0f * (sample(x, y, z + 1) - grayValue); - else if (z == sizeZm1) - gz = 2.0f * (grayValue - sample(x, y, z - 1)); - else - gz = sample(x, y, z + 1) - sample(x, y, z - 1); - - write(x, y, z, grayValue, gx, gy, gz); - } - - inline void compute1D(int y, int z) - { - int x; - - x = 0; - computeClamp(x, y, z); - x++; - - while (x < sizeX - 1) - { - compute(x, y, z); - x++; - } - - if (x < sizeX) - { - computeClamp(x, y, z); - x++; - } - - while (x < fullX) - { - fill(x, y, z); - x++; - } - } - - inline void fill1D(int y, int z) - { - int x; - - x = 0; - while (x < fullX) - { - fill(x, y, z); - x++; - } - } - - inline void computeClamp1D(int y, int z) - { - int x; - - x = 0; - - while (x < sizeX) - { - computeClamp(x, y, z); - x++; - } - - while (x < fullX) - { - fill(x, y, z); - x++; - } - } - - inline void computeClamp2D(int z) - { - int y; - - y = 0; - - while (y < sizeY) - { - computeClamp1D(y, z); - y++; - } - - while (y < fullY) - { - fill1D(y, z); - y++; - } - } - - inline void compute2D(int z) - { - int y; - - y = 0; - computeClamp1D(y, z); - y++; - - while (y < sizeY - 1) - { - compute1D(y, z); - y++; - } - - if (y < sizeY) - { - computeClamp1D(y, z); - y++; - } - - while (y < fullY) - { - fill1D(y, z); - y++; - } - } - - inline void fill2D(int z) - { - int y; - - y = 0; - while (y < fullY) - { - fill1D(y, z); - y++; - } - } - - inline void fillSlices(int currentChunkStart, int currentChunkEnd) - { - offZ = currentChunkStart; - -/* - int num = omp_get_num_procs(); - MITK_INFO << "omp uses " << num << " processors"; -*/ - -#pragma omp parallel for - for (int z = currentChunkStart; z <= currentChunkEnd; z++) - { - if (z == 0 || z == sizeZ - 1) - computeClamp2D(z); - else if (z >= sizeZ) - fill2D(z); - else - compute2D(z); - } - } -}; - -template -void vtkVolumeTextureMapper3DComputeScalars( - T *dataPtr, vtkMitkVolumeTextureMapper3D *me, float offset, float scale, GLuint volume1, GLuint /*volume2*/) -{ - // T *inPtr; - // unsigned char *outPtr, *outPtr2; - // int i, j, k; - // int idx; - - int inputDimensions[3]; - double inputSpacing[3]; - vtkImageData *input = me->GetInput(); - - input->GetDimensions(inputDimensions); - input->GetSpacing(inputSpacing); - - int outputDimensions[3]; - float outputSpacing[3]; - me->GetVolumeDimensions(outputDimensions); - me->GetVolumeSpacing(outputSpacing); - - // int components = input->GetNumberOfScalarComponents(); - - // double wx, wy, wz; - // double fx, fy, fz; - // int x, y, z; - - // double sampleRate[3]; - // sampleRate[0] = outputSpacing[0] / static_cast(inputSpacing[0]); - // sampleRate[1] = outputSpacing[1] / static_cast(inputSpacing[1]); - // sampleRate[2] = outputSpacing[2] / static_cast(inputSpacing[2]); - - int fullX = outputDimensions[0]; - int fullY = outputDimensions[1]; - int fullZ = outputDimensions[2]; - - int sizeX = inputDimensions[0]; - int sizeY = inputDimensions[1]; - int sizeZ = inputDimensions[2]; - - int chunkSize = 64; - - if (fullZ < chunkSize) - chunkSize = fullZ; - - int numChunks = (fullZ + (chunkSize - 1)) / chunkSize; - - // inPtr = dataPtr; - - unsigned char *tmpPtr = new unsigned char[fullX * fullY * chunkSize * 4]; - unsigned char *tmpPtr2 = 0; // new unsigned char[fullX*fullY*chunkSize*3]; - - // For each Chunk - { - ScalarGradientCompute sgc(dataPtr, tmpPtr, tmpPtr2, sizeX, sizeY, sizeZ, fullX, fullY, fullZ, offset, scale); - - int currentChunk = 0; - - while (currentChunk < numChunks) - { - int currentChunkStart = currentChunk * chunkSize; - int currentChunkEnd = currentChunkStart + chunkSize - 1; - - if (currentChunkEnd > (fullZ - 1)) - currentChunkEnd = (fullZ - 1); - - int currentChunkSize = currentChunkEnd - currentChunkStart + 1; - - sgc.fillSlices(currentChunkStart, currentChunkEnd); - - glBindTexture(vtkgl::TEXTURE_3D, volume1); - vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D, - 0, - 0, - 0, - currentChunkStart, - fullX, - fullY, - currentChunkSize, - GL_RGBA, - GL_UNSIGNED_BYTE, - tmpPtr); - currentChunk++; - } - } - - delete[] tmpPtr; -} - -class RGBACompute -{ - unsigned char *dataPtr; - unsigned char *tmpPtr; - unsigned char *tmpPtr2; - int sizeX; - int sizeY; - int sizeZ; - int sizeXY; - int sizeXm1; - int sizeYm1; - int sizeZm1; - int fullX; - int fullY; - int fullZ; - int fullXY; - // int currentChunkStart; - // int currentChunkEnd; - - int offZ; - -public: - RGBACompute(unsigned char *_dataPtr, - unsigned char *_tmpPtr, - unsigned char *_tmpPtr2, - int _sizeX, - int _sizeY, - int _sizeZ, - int _fullX, - int _fullY, - int _fullZ) - { - dataPtr = _dataPtr; - tmpPtr = _tmpPtr; - tmpPtr2 = _tmpPtr2; - sizeX = _sizeX; - sizeY = _sizeY; - sizeZ = _sizeZ; - fullX = _fullX; - fullY = _fullY; - fullZ = _fullZ; - - sizeXY = sizeX * sizeY; - sizeXm1 = sizeX - 1; - sizeYm1 = sizeY - 1; - sizeZm1 = sizeZ - 1; - - fullXY = fullX * fullY; - offZ = 0; - } - - inline int sample(int x, int y, int z) { return dataPtr[(x + y * sizeX + z * sizeXY) * 4 + 3]; } - inline void fill(int x, int y, int z) - { - int doff = x + y * fullX + (z - offZ) * fullXY; - - tmpPtr[doff * 4 + 0] = 0; - tmpPtr[doff * 4 + 1] = 0; - tmpPtr[doff * 4 + 2] = 0; - tmpPtr[doff * 4 + 3] = 0; - - tmpPtr2[doff * 3 + 0] = 0; - tmpPtr2[doff * 3 + 1] = 0; - tmpPtr2[doff * 3 + 2] = 0; - } - - inline int clamp(int x) - { - if (x < 0) - x = 0; - else if (x > 255) - x = 255; - return x; - } - - inline void write(int x, int y, int z, int iGrayValue, int gx, int gy, int gz) - { - /* - gx /= aspect[0]; - gy /= aspect[1]; - gz /= aspect[2]; - */ - int nx = static_cast(0.5f * gx + 127.5f); - int ny = static_cast(0.5f * gy + 127.5f); - int nz = static_cast(0.5f * gz + 127.5f); - - int doff = x + y * fullX + (z - offZ) * fullXY; - - // tmpPtr[doff*2+0]= 0; - - tmpPtr[doff * 4 + 0] = clamp(nx); - tmpPtr[doff * 4 + 1] = clamp(ny); - tmpPtr[doff * 4 + 2] = clamp(nz); - tmpPtr[doff * 4 + 3] = clamp(iGrayValue); - - int soff = x + y * sizeX + z * sizeXY; - - tmpPtr2[doff * 3 + 0] = dataPtr[soff * 4 + 0]; - tmpPtr2[doff * 3 + 1] = dataPtr[soff * 4 + 1]; - tmpPtr2[doff * 3 + 2] = dataPtr[soff * 4 + 2]; - - /* - if( z == fullZ/2 ) - if( y == fullY/2 ) - MITK_INFO << x << " " << y << " " << z << " : " << iGrayValue << " : " << iGradient; - */ - } - - inline void compute(int x, int y, int z) - { - int grayValue = sample(x, y, z); - int gx, gy, gz; - - gx = sample(x + 1, y, z) - sample(x - 1, y, z); - gy = sample(x, y + 1, z) - sample(x, y - 1, z); - gz = sample(x, y, z + 1) - sample(x, y, z - 1); - - write(x, y, z, grayValue, gx, gy, gz); - } - - inline void computeClamp(int x, int y, int z) - { - int grayValue = sample(x, y, z); - int gx, gy, gz; - - if (x == 0) - gx = 2 * (sample(x + 1, y, z) - grayValue); - else if (x == sizeXm1) - gx = 2 * (grayValue - sample(x - 1, y, z)); - else - gx = sample(x + 1, y, z) - sample(x - 1, y, z); - - if (y == 0) - gy = 2 * (sample(x, y + 1, z) - grayValue); - else if (y == sizeYm1) - gy = 2 * (grayValue - sample(x, y - 1, z)); - else - gy = sample(x, y + 1, z) - sample(x, y - 1, z); - - if (z == 0) - gz = 2 * (sample(x, y, z + 1) - grayValue); - else if (z == sizeZm1) - gz = 2 * (grayValue - sample(x, y, z - 1)); - else - gz = sample(x, y, z + 1) - sample(x, y, z - 1); - - write(x, y, z, grayValue, gx, gy, gz); - } - - inline void compute1D(int y, int z) - { - int x = 0; - - computeClamp(x, y, z); - x++; - - while (x < sizeX - 1) - { - compute(x, y, z); - x++; - } - - if (x < sizeX) - { - computeClamp(x, y, z); - x++; - } - - while (x < fullX) - { - fill(x, y, z); - x++; - } - } - - inline void fill1D(int y, int z) - { - int x = 0; - - while (x < fullX) - { - fill(x, y, z); - x++; - } - } - - inline void computeClamp1D(int y, int z) - { - int x = 0; - - while (x < sizeX) - { - computeClamp(x, y, z); - x++; - } - - while (x < fullX) - { - fill(x, y, z); - x++; - } - } - - inline void computeClamp2D(int z) - { - int y = 0; - - while (y < sizeY) - { - computeClamp1D(y, z); - y++; - } - - while (y < fullY) - { - fill1D(y, z); - y++; - } - } - - inline void compute2D(int z) - { - int y = 0; - - computeClamp1D(y, z); - y++; - - while (y < sizeY - 1) - { - compute1D(y, z); - y++; - } - - if (y < sizeY) - { - computeClamp1D(y, z); - y++; - } - - while (y < fullY) - { - fill1D(y, z); - y++; - } - } - - inline void fill2D(int z) - { - int y = 0; - - while (y < fullY) - { - fill1D(y, z); - y++; - } - } - - inline void fillSlices(int currentChunkStart, int currentChunkEnd) - { - offZ = currentChunkStart; - -#pragma omp parallel for - for (int z = currentChunkStart; z <= currentChunkEnd; z++) - { - if (z == 0 || z == sizeZ - 1) - computeClamp2D(z); - else if (z >= sizeZ) - fill2D(z); - else - compute2D(z); - } - } -}; - -void vtkVolumeTextureMapper3DComputeRGBA(unsigned char *dataPtr, - vtkMitkVolumeTextureMapper3D *me, - GLuint volume1, - GLuint volume2) -{ - // unsigned char *inPtr; - // unsigned char *outPtr, *outPtr2; - // int i, j, k; - // int idx; - - int inputDimensions[3]; - double inputSpacing[3]; - vtkImageData *input = me->GetInput(); - - input->GetDimensions(inputDimensions); - input->GetSpacing(inputSpacing); - - int outputDimensions[3]; - float outputSpacing[3]; - me->GetVolumeDimensions(outputDimensions); - me->GetVolumeSpacing(outputSpacing); - - int components = input->GetNumberOfScalarComponents(); - - MITK_INFO << "components are " << components; - - // double wx, wy, wz; - // double fx, fy, fz; - // int x, y, z; - - // double sampleRate[3]; - // sampleRate[0] = outputSpacing[0] / static_cast(inputSpacing[0]); - // sampleRate[1] = outputSpacing[1] / static_cast(inputSpacing[1]); - // sampleRate[2] = outputSpacing[2] / static_cast(inputSpacing[2]); - - int fullX = outputDimensions[0]; - int fullY = outputDimensions[1]; - int fullZ = outputDimensions[2]; - - int sizeX = inputDimensions[0]; - int sizeY = inputDimensions[1]; - int sizeZ = inputDimensions[2]; - - int chunkSize = 64; - - if (fullZ < chunkSize) - chunkSize = fullZ; - - int numChunks = (fullZ + (chunkSize - 1)) / chunkSize; - - // inPtr = dataPtr; - - unsigned char *tmpPtr = new unsigned char[fullX * fullY * chunkSize * 4]; - unsigned char *tmpPtr2 = new unsigned char[fullX * fullY * chunkSize * 3]; - - // For each Chunk - { - RGBACompute sgc(dataPtr, tmpPtr, tmpPtr2, sizeX, sizeY, sizeZ, fullX, fullY, fullZ); - - int currentChunk = 0; - - while (currentChunk < numChunks) - { - // MITK_INFO << "processing chunk " << currentChunk; - - int currentChunkStart = currentChunk * chunkSize; - int currentChunkEnd = currentChunkStart + chunkSize - 1; - - if (currentChunkEnd > (fullZ - 1)) - currentChunkEnd = (fullZ - 1); - - int currentChunkSize = currentChunkEnd - currentChunkStart + 1; - - sgc.fillSlices(currentChunkStart, currentChunkEnd); - - glBindTexture(vtkgl::TEXTURE_3D, volume1); - vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D, - 0, - 0, - 0, - currentChunkStart, - fullX, - fullY, - currentChunkSize, - GL_RGBA, - GL_UNSIGNED_BYTE, - tmpPtr); - - glBindTexture(vtkgl::TEXTURE_3D, volume2); - vtkgl::TexSubImage3D(vtkgl::TEXTURE_3D, - 0, - 0, - 0, - currentChunkStart, - fullX, - fullY, - currentChunkSize, - GL_RGB, - GL_UNSIGNED_BYTE, - tmpPtr2); - - currentChunk++; - } - } - - delete[] tmpPtr; - delete[] tmpPtr2; -} - -//----------------------------------------------------------------------------- -void vtkMitkOpenGLVolumeTextureMapper3D::ComputeVolumeDimensions() -{ - // Get the image data - vtkImageData *input = this->GetInput(); - - // How big does the Volume need to be? - int dim[3]; - input->GetDimensions(dim); - - int powerOfTwoDim[3]; - - if (this->SupportsNonPowerOfTwoTextures) - { - for (int i = 0; i < 3; i++) - powerOfTwoDim[i] = (dim[i] + 1) & ~1; - - // MITK_INFO << "using non-power-two even textures (" << - // (1.0-double(dim[0]*dim[1]*dim[2])/double(powerOfTwoDim[0]*powerOfTwoDim[1]*powerOfTwoDim[2])) * 100.0 << "% - // memory wasted)"; - } - else - { - for (int i = 0; i < 3; i++) - { - powerOfTwoDim[i] = 4; - while (powerOfTwoDim[i] < dim[i]) - powerOfTwoDim[i] *= 2; - } - - MITK_WARN << "using power-two textures (" - << (1.0 - - double(dim[0] * dim[1] * dim[2]) / double(powerOfTwoDim[0] * powerOfTwoDim[1] * powerOfTwoDim[2])) * - 100.0 - << "% memory wasted)"; - } - - // Save the volume size - this->VolumeDimensions[0] = powerOfTwoDim[0]; - this->VolumeDimensions[1] = powerOfTwoDim[1]; - this->VolumeDimensions[2] = powerOfTwoDim[2]; - - // What is the spacing? - double spacing[3]; - input->GetSpacing(spacing); - - // Compute the new spacing - this->VolumeSpacing[0] = (dim[0] - 1.01) * spacing[0] / static_cast(this->VolumeDimensions[0] - 1); - this->VolumeSpacing[1] = (dim[1] - 1.01) * spacing[1] / static_cast(this->VolumeDimensions[1] - 1); - this->VolumeSpacing[2] = ((dim[2]) - 1.01) * spacing[2] / static_cast(this->VolumeDimensions[2] - 1); -} - -//----------------------------------------------------------------------------- -bool vtkMitkOpenGLVolumeTextureMapper3D::UpdateVolumes(vtkVolume *vtkNotUsed(vol)) -{ - // Get the image data - vtkImageData *input = this->GetInput(); - // input->Update(); //VTK6_TODO - - bool needUpdate = false; - - // Has the volume changed in some way? - if (this->SavedTextureInput != input || this->SavedTextureMTime.GetMTime() < input->GetMTime()) - needUpdate = true; - - // Do we have any volume on the gpu already? - if (!this->Volume1Index) - needUpdate = true; - - if (!needUpdate) - return true; - - ComputeVolumeDimensions(); - - int components = input->GetNumberOfScalarComponents(); - - // Find the scalar range - double scalarRange[2]; - input->GetPointData()->GetScalars()->GetRange(scalarRange, components - 1); - - // Is the difference between max and min less than 4096? If so, and if - // the data is not of float or double type, use a simple offset mapping. - // If the difference between max and min is 4096 or greater, or the data - // is of type float or double, we must use an offset / scaling mapping. - // In this case, the array size will be 4096 - we need to figure out the - // offset and scale factor. - float offset; - float scale; - - int arraySizeNeeded; - - int scalarType = input->GetScalarType(); - - if (scalarType == VTK_FLOAT || scalarType == VTK_DOUBLE || scalarRange[1] - scalarRange[0] > 255) - { - arraySizeNeeded = 256; - offset = -scalarRange[0]; - scale = 255.0 / (scalarRange[1] - scalarRange[0]); - } - else - { - arraySizeNeeded = static_cast(scalarRange[1] - scalarRange[0] + 1); - offset = -scalarRange[0]; - scale = 1.0; - } - - this->ColorTableSize = arraySizeNeeded; - this->ColorTableOffset = offset; - this->ColorTableScale = scale; - - // Allocating volume on gpu - { - // Deleting old textures - this->DeleteTextureIndex(&this->Volume1Index); - this->DeleteTextureIndex(&this->Volume2Index); - this->DeleteTextureIndex(&this->Volume3Index); - - this->CreateTextureIndex(&this->Volume1Index); - // this->CreateTextureIndex(&this->Volume2Index); - - int dim[3]; - this->GetVolumeDimensions(dim); - - vtkgl::ActiveTexture(vtkgl::TEXTURE0); - - MITK_INFO << "allocating volume on gpu"; - - GLint gradientScalarTextureFormat = GL_RGBA8; - - if (this->UseCompressedTexture && SupportsCompressedTexture) - gradientScalarTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - - glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); - vtkgl::TexImage3D( - vtkgl::TEXTURE_3D, 0, gradientScalarTextureFormat, dim[0], dim[1], dim[2], 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - this->Setup3DTextureParameters(true); - } - - // Transfer the input volume to the RGBA volume - void *dataPtr = input->GetScalarPointer(); - - switch (scalarType) - { - vtkTemplateMacro(vtkVolumeTextureMapper3DComputeScalars( - static_cast(dataPtr), this, offset, scale, this->Volume1Index, this->Volume2Index)); - } - - this->SavedTextureInput = input; - this->SavedTextureMTime.Modified(); - - return true; -} - -//----------------------------------------------------------------------------- -bool vtkMitkOpenGLVolumeTextureMapper3D::UpdateVolumesRGBA(vtkVolume *vtkNotUsed(vol)) -{ - // Get the image data - vtkImageData *input = this->GetInput(); - // input->Update(); //VTK6_TODO - - bool needUpdate = false; - - // Has the volume changed in some way? - if (this->SavedTextureInput != input || this->SavedTextureMTime.GetMTime() < input->GetMTime()) - needUpdate = true; - - // Do we have any volume on the gpu already? - if (!this->Volume1Index) - needUpdate = true; - - if (!needUpdate) - return true; - - MITK_INFO << "updating rgba volume"; - - ComputeVolumeDimensions(); - - // Allocating volume on gpu - { - // Deleting old textures - this->DeleteTextureIndex(&this->Volume1Index); - this->DeleteTextureIndex(&this->Volume2Index); - this->DeleteTextureIndex(&this->Volume3Index); - - this->CreateTextureIndex(&this->Volume1Index); - this->CreateTextureIndex(&this->Volume2Index); - - int dim[3]; - this->GetVolumeDimensions(dim); - - MITK_INFO << "allocating volume on gpu"; - - GLint gradientScalarTextureFormat = GL_RGBA8; - GLint colorTextureFormat = GL_RGB8; - - if (this->UseCompressedTexture && SupportsCompressedTexture) - { - gradientScalarTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - colorTextureFormat = myGL_COMPRESSED_RGB_S3TC_DXT1_EXT; - } - - vtkgl::ActiveTexture(vtkgl::TEXTURE0); - glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); - vtkgl::TexImage3D( - vtkgl::TEXTURE_3D, 0, gradientScalarTextureFormat, dim[0], dim[1], dim[2], 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); - this->Setup3DTextureParameters(true); - - glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); - vtkgl::TexImage3D(vtkgl::TEXTURE_3D, 0, colorTextureFormat, dim[0], dim[1], dim[2], 0, GL_RGB, GL_UNSIGNED_BYTE, 0); - this->Setup3DTextureParameters(true); - } - - // Transfer the input volume to the RGBA volume - unsigned char *dataPtr = (unsigned char *)input->GetScalarPointer(); - vtkVolumeTextureMapper3DComputeRGBA(dataPtr, this, this->Volume1Index, this->Volume2Index); - - this->SavedTextureInput = input; - this->SavedTextureMTime.Modified(); - - return true; -} - -void vtkMitkOpenGLVolumeTextureMapper3D::Setup3DTextureParameters(bool linear) -{ - // GPU_INFO << "Setup3DTextureParameters"; - - if (linear) - { - glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - } - else - { - glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - } - - glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameterf(vtkgl::TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP); -} - -void vtkMitkOpenGLVolumeTextureMapper3D::SetupOneIndependentTextures(vtkRenderer *vtkNotUsed(ren), vtkVolume *vol) -{ - // Update the volume containing the 2 byte scalar / gradient magnitude - this->UpdateVolumes(vol); - - // Update the dependent 2D color table mapping scalar value and - // gradient magnitude to RGBA - if (this->UpdateColorLookup(vol) || !this->ColorLookupIndex) - { - this->DeleteTextureIndex(&this->ColorLookupIndex); - this->DeleteTextureIndex(&this->AlphaLookupIndex); - - this->CreateTextureIndex(&this->ColorLookupIndex); - - vtkgl::ActiveTexture(vtkgl::TEXTURE1); - glBindTexture(GL_TEXTURE_2D, this->ColorLookupIndex); - - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); - glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); - - // MITK_INFO << "uploading transferfunction"; - - GLint colorLookupTextureFormat = GL_RGBA8; - - if (this->UseCompressedTexture && SupportsCompressedTexture) - colorLookupTextureFormat = myGL_COMPRESSED_RGBA_S3TC_DXT5_EXT; - - glTexImage2D(GL_TEXTURE_2D, 0, colorLookupTextureFormat, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, this->ColorLookup); - } -} - -void vtkMitkOpenGLVolumeTextureMapper3D::SetupRGBATextures(vtkRenderer *vtkNotUsed(ren), vtkVolume *vol) -{ - MITK_INFO << "SetupFourDependentTextures"; - - this->UpdateVolumesRGBA(vol); - - /* -vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); -glDisable( GL_TEXTURE_2D ); -glEnable( vtkgl::TEXTURE_3D ); - -vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); -glDisable( GL_TEXTURE_2D ); -glEnable( vtkgl::TEXTURE_3D ); - -vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); -glDisable( GL_TEXTURE_2D ); -glEnable( vtkgl::TEXTURE_3D ); - -// Update the volume containing the 3 byte scalars / gradient magnitude -if ( this->UpdateVolumes( vol ) || !this->Volume1Index || - !this->Volume2Index || !this->Volume3Index ) - { - int dim[3]; - this->GetVolumeDimensions(dim); - - vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); - glBindTexture(vtkgl::TEXTURE_3D,0); - this->DeleteTextureIndex(&this->Volume1Index); - this->CreateTextureIndex(&this->Volume1Index); - glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); - vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalRGB,dim[0],dim[1], - dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,this->Volume1); - - vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); - glBindTexture(vtkgl::TEXTURE_3D,0); - this->DeleteTextureIndex(&this->Volume2Index); - this->CreateTextureIndex(&this->Volume2Index); - glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); - vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalLA,dim[0],dim[1], - dim[2],0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE, - this->Volume2); - - vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); - glBindTexture(vtkgl::TEXTURE_3D,0); - this->DeleteTextureIndex(&this->Volume3Index); - this->CreateTextureIndex(&this->Volume3Index); - glBindTexture(vtkgl::TEXTURE_3D, this->Volume3Index); - vtkgl::TexImage3D(vtkgl::TEXTURE_3D,0,this->InternalRGB,dim[0],dim[1], - dim[2],0,GL_RGB,GL_UNSIGNED_BYTE,this->Volume3); - } - -vtkgl::ActiveTexture( vtkgl::TEXTURE0 ); -glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); -this->Setup3DTextureParameters( true ); - -vtkgl::ActiveTexture( vtkgl::TEXTURE1 ); -glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); -this->Setup3DTextureParameters( true ); - -vtkgl::ActiveTexture( vtkgl::TEXTURE2 ); -glBindTexture(vtkgl::TEXTURE_3D_EXT, this->Volume3Index); -this->Setup3DTextureParameters( true ); - -vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); -glEnable( GL_TEXTURE_2D ); -glDisable( vtkgl::TEXTURE_3D ); - -// Update the dependent 2D table mapping scalar value and -// gradient magnitude to opacity -if ( this->UpdateColorLookup( vol ) || !this->AlphaLookupIndex ) - { - this->DeleteTextureIndex(&this->ColorLookupIndex); - - vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); - glBindTexture(GL_TEXTURE_2D,0); - this->DeleteTextureIndex(&this->AlphaLookupIndex); - this->CreateTextureIndex(&this->AlphaLookupIndex); - glBindTexture(GL_TEXTURE_2D, this->AlphaLookupIndex); - - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP ); - glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP ); - - //MITK_INFO << "uploading transferfunction"; - - glTexImage2D(GL_TEXTURE_2D,0,this->InternalAlpha, 256, 256, 0, - GL_ALPHA, GL_UNSIGNED_BYTE, this->AlphaLookup ); - } - -vtkgl::ActiveTexture( vtkgl::TEXTURE3 ); -glBindTexture(GL_TEXTURE_2D, this->AlphaLookupIndex); - -*/ -} - -void vtkMitkOpenGLVolumeTextureMapper3D::RenderOneIndependentShadeFP(vtkRenderer *ren, vtkVolume *vol) -{ - // GPU_INFO << "RenderOneIndependentShadeFP"; - - this->SetupOneIndependentTextures(ren, vol); - - glEnable(vtkgl::FRAGMENT_PROGRAM_ARB); - - vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, prgOneComponentShade); - - this->SetupProgramLocalsForShadingFP(ren, vol); - - // Bind Textures - { - vtkgl::ActiveTexture(vtkgl::TEXTURE0); - glDisable(GL_TEXTURE_2D); - glEnable(vtkgl::TEXTURE_3D); - glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); - - vtkgl::ActiveTexture(vtkgl::TEXTURE1); - glEnable(GL_TEXTURE_2D); - glDisable(vtkgl::TEXTURE_3D); - glBindTexture(GL_TEXTURE_2D, this->ColorLookupIndex); - - vtkgl::ActiveTexture(vtkgl::TEXTURE2); - glDisable(GL_TEXTURE_2D); - glEnable(vtkgl::TEXTURE_3D); - glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); - } - - int stages[4] = {1, 1, 1, 0}; - this->RenderPolygons(ren, vol, stages); - - glDisable(vtkgl::FRAGMENT_PROGRAM_ARB); -} - -void vtkMitkOpenGLVolumeTextureMapper3D::RenderRGBAShadeFP(vtkRenderer *ren, vtkVolume *vol) -{ - this->SetupRGBATextures(ren, vol); - - glEnable(vtkgl::FRAGMENT_PROGRAM_ARB); - - vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, prgRGBAShade); - - this->SetupProgramLocalsForShadingFP(ren, vol); - - // Bind Textures - { - vtkgl::ActiveTexture(vtkgl::TEXTURE0); - glDisable(GL_TEXTURE_2D); - glEnable(vtkgl::TEXTURE_3D); - glBindTexture(vtkgl::TEXTURE_3D, this->Volume1Index); - - vtkgl::ActiveTexture(vtkgl::TEXTURE1); - glDisable(GL_TEXTURE_2D); - glEnable(vtkgl::TEXTURE_3D); - glBindTexture(vtkgl::TEXTURE_3D, this->Volume2Index); - } - - int stages[4] = {1, 1, 1, 0}; - this->RenderPolygons(ren, vol, stages); - - glDisable(vtkgl::FRAGMENT_PROGRAM_ARB); -} - -void vtkMitkOpenGLVolumeTextureMapper3D::GetLightInformation(vtkRenderer *ren, - vtkVolume *vol, - GLfloat lightDirection[2][4], - GLfloat lightDiffuseColor[2][4], - GLfloat lightSpecularColor[2][4], - GLfloat halfwayVector[2][4], - GLfloat ambientColor[4]) -{ - // GPU_INFO << "GetLightInformation"; - - float ambient = vol->GetProperty()->GetAmbient(); - float diffuse = vol->GetProperty()->GetDiffuse(); - float specular = vol->GetProperty()->GetSpecular(); - - vtkTransform *volumeTransform = vtkTransform::New(); - - volumeTransform->SetMatrix(vol->GetMatrix()); - volumeTransform->Inverse(); - - vtkLightCollection *lights = ren->GetLights(); - lights->InitTraversal(); - - vtkLight *light[2]; - light[0] = lights->GetNextItem(); - light[1] = lights->GetNextItem(); - - int lightIndex = 0; - - double cameraPosition[3]; - double cameraFocalPoint[3]; - - ren->GetActiveCamera()->GetPosition(cameraPosition); - ren->GetActiveCamera()->GetFocalPoint(cameraFocalPoint); - - double viewDirection[3]; - - volumeTransform->TransformPoint(cameraPosition, cameraPosition); - volumeTransform->TransformPoint(cameraFocalPoint, cameraFocalPoint); - - viewDirection[0] = cameraFocalPoint[0] - cameraPosition[0]; - viewDirection[1] = cameraFocalPoint[1] - cameraPosition[1]; - viewDirection[2] = cameraFocalPoint[2] - cameraPosition[2]; - - vtkMath::Normalize(viewDirection); - - ambientColor[0] = 0.0; - ambientColor[1] = 0.0; - ambientColor[2] = 0.0; - ambientColor[3] = 0.0; - - for (lightIndex = 0; lightIndex < 2; lightIndex++) - { - float dir[3] = {0, 0, 0}; - float half[3] = {0, 0, 0}; - - if (light[lightIndex] == nullptr || light[lightIndex]->GetSwitch() == 0) - { - lightDiffuseColor[lightIndex][0] = 0.0; - lightDiffuseColor[lightIndex][1] = 0.0; - lightDiffuseColor[lightIndex][2] = 0.0; - lightDiffuseColor[lightIndex][3] = 0.0; - - lightSpecularColor[lightIndex][0] = 0.0; - lightSpecularColor[lightIndex][1] = 0.0; - lightSpecularColor[lightIndex][2] = 0.0; - lightSpecularColor[lightIndex][3] = 0.0; - } - else - { - float lightIntensity = light[lightIndex]->GetIntensity(); - double lightColor[3]; - - light[lightIndex]->GetDiffuseColor(lightColor); - - double lightPosition[3]; - double lightFocalPoint[3]; - light[lightIndex]->GetTransformedPosition(lightPosition); - light[lightIndex]->GetTransformedFocalPoint(lightFocalPoint); - - volumeTransform->TransformPoint(lightPosition, lightPosition); - volumeTransform->TransformPoint(lightFocalPoint, lightFocalPoint); - - dir[0] = lightPosition[0] - lightFocalPoint[0]; - dir[1] = lightPosition[1] - lightFocalPoint[1]; - dir[2] = lightPosition[2] - lightFocalPoint[2]; - - vtkMath::Normalize(dir); - - lightDiffuseColor[lightIndex][0] = lightColor[0] * diffuse * lightIntensity; - lightDiffuseColor[lightIndex][1] = lightColor[1] * diffuse * lightIntensity; - lightDiffuseColor[lightIndex][2] = lightColor[2] * diffuse * lightIntensity; - lightDiffuseColor[lightIndex][3] = 1.0; - - lightSpecularColor[lightIndex][0] = lightColor[0] * specular * lightIntensity; - lightSpecularColor[lightIndex][1] = lightColor[1] * specular * lightIntensity; - lightSpecularColor[lightIndex][2] = lightColor[2] * specular * lightIntensity; - lightSpecularColor[lightIndex][3] = 0.0; - - half[0] = dir[0] - viewDirection[0]; - half[1] = dir[1] - viewDirection[1]; - half[2] = dir[2] - viewDirection[2]; - - vtkMath::Normalize(half); - - ambientColor[0] += ambient * lightColor[0]; - ambientColor[1] += ambient * lightColor[1]; - ambientColor[2] += ambient * lightColor[2]; - } - - lightDirection[lightIndex][0] = (dir[0] + 1.0) / 2.0; - lightDirection[lightIndex][1] = (dir[1] + 1.0) / 2.0; - lightDirection[lightIndex][2] = (dir[2] + 1.0) / 2.0; - lightDirection[lightIndex][3] = 0.0; - - halfwayVector[lightIndex][0] = (half[0] + 1.0) / 2.0; - halfwayVector[lightIndex][1] = (half[1] + 1.0) / 2.0; - halfwayVector[lightIndex][2] = (half[2] + 1.0) / 2.0; - halfwayVector[lightIndex][3] = 0.0; - } - - volumeTransform->Delete(); -} - -void vtkMitkOpenGLVolumeTextureMapper3D::SetupProgramLocalsForShadingFP(vtkRenderer *ren, vtkVolume *vol) -{ - // GPU_INFO << "SetupProgramLocalsForShadingFP"; - - GLfloat lightDirection[2][4]; - GLfloat lightDiffuseColor[2][4]; - GLfloat lightSpecularColor[2][4]; - GLfloat halfwayVector[2][4]; - GLfloat ambientColor[4]; - - float ambient = vol->GetProperty()->GetAmbient(); - float diffuse = vol->GetProperty()->GetDiffuse(); - float specular = vol->GetProperty()->GetSpecular(); - float specularPower = vol->GetProperty()->GetSpecularPower(); - - vtkTransform *volumeTransform = vtkTransform::New(); - - volumeTransform->SetMatrix(vol->GetMatrix()); - volumeTransform->Inverse(); - - vtkLightCollection *lights = ren->GetLights(); - lights->InitTraversal(); - - vtkLight *light[2]; - light[0] = lights->GetNextItem(); - light[1] = lights->GetNextItem(); - - int lightIndex = 0; - - double cameraPosition[3]; - double cameraFocalPoint[3]; - - ren->GetActiveCamera()->GetPosition(cameraPosition); - ren->GetActiveCamera()->GetFocalPoint(cameraFocalPoint); - - volumeTransform->TransformPoint(cameraPosition, cameraPosition); - volumeTransform->TransformPoint(cameraFocalPoint, cameraFocalPoint); - - double viewDirection[4]; - - viewDirection[0] = cameraFocalPoint[0] - cameraPosition[0]; - viewDirection[1] = cameraFocalPoint[1] - cameraPosition[1]; - viewDirection[2] = cameraFocalPoint[2] - cameraPosition[2]; - viewDirection[3] = 0.0; - - vtkMath::Normalize(viewDirection); - - ambientColor[0] = 0.0; - ambientColor[1] = 0.0; - ambientColor[2] = 0.0; - ambientColor[3] = 0.0; - - for (lightIndex = 0; lightIndex < 2; lightIndex++) - { - float dir[3] = {0, 0, 0}; - float half[3] = {0, 0, 0}; - - if (light[lightIndex] == nullptr || light[lightIndex]->GetSwitch() == 0) - { - lightDiffuseColor[lightIndex][0] = 0.0; - lightDiffuseColor[lightIndex][1] = 0.0; - lightDiffuseColor[lightIndex][2] = 0.0; - lightDiffuseColor[lightIndex][3] = 0.0; - - lightSpecularColor[lightIndex][0] = 0.0; - lightSpecularColor[lightIndex][1] = 0.0; - lightSpecularColor[lightIndex][2] = 0.0; - lightSpecularColor[lightIndex][3] = 0.0; - } - else - { - float lightIntensity = light[lightIndex]->GetIntensity(); - double lightColor[3]; - - light[lightIndex]->GetDiffuseColor(lightColor); - - double lightPosition[3]; - double lightFocalPoint[3]; - light[lightIndex]->GetTransformedPosition(lightPosition); - light[lightIndex]->GetTransformedFocalPoint(lightFocalPoint); - - volumeTransform->TransformPoint(lightPosition, lightPosition); - volumeTransform->TransformPoint(lightFocalPoint, lightFocalPoint); - - dir[0] = lightPosition[0] - lightFocalPoint[0]; - dir[1] = lightPosition[1] - lightFocalPoint[1]; - dir[2] = lightPosition[2] - lightFocalPoint[2]; - - vtkMath::Normalize(dir); - - lightDiffuseColor[lightIndex][0] = lightColor[0] * diffuse * lightIntensity; - lightDiffuseColor[lightIndex][1] = lightColor[1] * diffuse * lightIntensity; - lightDiffuseColor[lightIndex][2] = lightColor[2] * diffuse * lightIntensity; - lightDiffuseColor[lightIndex][3] = 0.0; - - lightSpecularColor[lightIndex][0] = lightColor[0] * specular * lightIntensity; - lightSpecularColor[lightIndex][1] = lightColor[1] * specular * lightIntensity; - lightSpecularColor[lightIndex][2] = lightColor[2] * specular * lightIntensity; - lightSpecularColor[lightIndex][3] = 0.0; - - half[0] = dir[0] - viewDirection[0]; - half[1] = dir[1] - viewDirection[1]; - half[2] = dir[2] - viewDirection[2]; - - vtkMath::Normalize(half); - - ambientColor[0] += ambient * lightColor[0]; - ambientColor[1] += ambient * lightColor[1]; - ambientColor[2] += ambient * lightColor[2]; - } - - lightDirection[lightIndex][0] = dir[0]; - lightDirection[lightIndex][1] = dir[1]; - lightDirection[lightIndex][2] = dir[2]; - lightDirection[lightIndex][3] = 0.0; - - halfwayVector[lightIndex][0] = half[0]; - halfwayVector[lightIndex][1] = half[1]; - halfwayVector[lightIndex][2] = half[2]; - halfwayVector[lightIndex][3] = 0.0; - } - - volumeTransform->Delete(); - - vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, - 0, - lightDirection[0][0], - lightDirection[0][1], - lightDirection[0][2], - lightDirection[0][3]); - - vtkgl::ProgramLocalParameter4fARB( - vtkgl::FRAGMENT_PROGRAM_ARB, 1, halfwayVector[0][0], halfwayVector[0][1], halfwayVector[0][2], halfwayVector[0][3]); - - vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, 2, ambient, diffuse, specular, specularPower); - - vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, - 3, - lightDiffuseColor[0][0], - lightDiffuseColor[0][1], - lightDiffuseColor[0][2], - lightDiffuseColor[0][3]); - - vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, - 4, - lightSpecularColor[0][0], - lightSpecularColor[0][1], - lightSpecularColor[0][2], - lightSpecularColor[0][3]); - - vtkgl::ProgramLocalParameter4fARB( - vtkgl::FRAGMENT_PROGRAM_ARB, 5, viewDirection[0], viewDirection[1], viewDirection[2], viewDirection[3]); - - vtkgl::ProgramLocalParameter4fARB(vtkgl::FRAGMENT_PROGRAM_ARB, 6, 2.0, -1.0, 0.0, 0.0); -} - -int vtkMitkOpenGLVolumeTextureMapper3D::IsRenderSupported(vtkRenderer *renderer, vtkVolumeProperty * /*property*/) -{ - // GPU_INFO << "IsRenderSupported"; - - if (!this->Initialized) - { - // this->Initialize(); - this->Initialize(renderer); - } - - if (!this->RenderPossible) - { - MITK_WARN << "vtkMitkOpenGLVolumeTextureMapper3D::IsRenderSupported Rendering not possible"; - return 0; - } - - if (!this->GetInput()) - { - MITK_WARN << "vtkMitkOpenGLVolumeTextureMapper3D::IsRenderSupported No input available"; - return 0; - } - - return 1; -} - -void vtkMitkOpenGLVolumeTextureMapper3D::Initialize(vtkRenderer *renderer) -{ - // GPU_INFO << "Initialize"; - - this->Initialized = 1; - // vtkOpenGLExtensionManager * extensions = vtkOpenGLExtensionManager::New(); - // extensions->SetRenderWindow(nullptr); // set render window to the current one. - vtkOpenGLExtensionManager *extensions = - static_cast(renderer->GetRenderWindow())->GetExtensionManager(); - - int supports_texture3D = extensions->ExtensionSupported("GL_VERSION_1_2"); - if (supports_texture3D) - { - extensions->LoadExtension("GL_VERSION_1_2"); - } - else - { - supports_texture3D = extensions->ExtensionSupported("GL_EXT_texture3D"); - if (supports_texture3D) - { - extensions->LoadCorePromotedExtension("GL_EXT_texture3D"); - } - } - - int supports_multitexture = extensions->ExtensionSupported("GL_VERSION_1_3"); - if (supports_multitexture) - { - extensions->LoadExtension("GL_VERSION_1_3"); - } - else - { - supports_multitexture = extensions->ExtensionSupported("GL_ARB_multitexture"); - if (supports_multitexture) - { - extensions->LoadCorePromotedExtension("GL_ARB_multitexture"); - } - } - - this->SupportsCompressedTexture = extensions->ExtensionSupported("GL_VERSION_1_3") == 1; - if (!this->SupportsCompressedTexture) - { - this->SupportsCompressedTexture = extensions->ExtensionSupported("GL_ARB_texture_compression") == 1; - if (this->SupportsCompressedTexture) - { - extensions->LoadCorePromotedExtension("GL_ARB_texture_compression"); - } - } - // GPU_INFO(this->SupportsCompressedTexture) << "supporting compressed textures"; - - this->SupportsNonPowerOfTwoTextures = extensions->ExtensionSupported("GL_VERSION_2_0") || - extensions->ExtensionSupported("GL_ARB_texture_non_power_of_two"); - - // GPU_INFO << "np2: " << (this->SupportsNonPowerOfTwoTextures?1:0); - - int supports_GL_ARB_fragment_program = extensions->ExtensionSupported("GL_ARB_fragment_program"); - if (supports_GL_ARB_fragment_program) - { - extensions->LoadExtension("GL_ARB_fragment_program"); - } - - int supports_GL_ARB_vertex_program = extensions->ExtensionSupported("GL_ARB_vertex_program"); - if (supports_GL_ARB_vertex_program) - { - extensions->LoadExtension("GL_ARB_vertex_program"); - } - - RenderPossible = 0; - - if (supports_texture3D && supports_multitexture && supports_GL_ARB_fragment_program && - supports_GL_ARB_vertex_program && vtkgl::TexImage3D && vtkgl::ActiveTexture && vtkgl::MultiTexCoord3fv && - vtkgl::GenProgramsARB && vtkgl::DeleteProgramsARB && vtkgl::BindProgramARB && vtkgl::ProgramStringARB && - vtkgl::ProgramLocalParameter4fARB) - { - RenderPossible = 1; - } - else - { - std::string errString = - "no gpu-acceleration possible cause following extensions/methods are missing or unsupported:"; - if (!supports_texture3D) - errString += " EXT_TEXTURE3D"; - if (!supports_multitexture) - errString += " EXT_MULTITEXTURE"; - if (!supports_GL_ARB_fragment_program) - errString += " ARB_FRAGMENT_PROGRAM"; - if (!supports_GL_ARB_vertex_program) - errString += " ARB_VERTEX_PROGRAM"; - if (!vtkgl::TexImage3D) - errString += " glTexImage3D"; - if (!vtkgl::ActiveTexture) - errString += " glActiveTexture"; - if (!vtkgl::MultiTexCoord3fv) - errString += " glMultiTexCoord3fv"; - if (!vtkgl::GenProgramsARB) - errString += " glGenProgramsARB"; - if (!vtkgl::DeleteProgramsARB) - errString += " glDeleteProgramsARB"; - if (!vtkgl::BindProgramARB) - errString += " glBindProgramARB"; - if (!vtkgl::ProgramStringARB) - errString += " glProgramStringARB"; - if (!vtkgl::ProgramLocalParameter4fARB) - errString += " glProgramLocalParameter4fARB"; - GPU_WARN << errString; - }; - - if (RenderPossible) - { - vtkgl::GenProgramsARB(1, &prgOneComponentShade); - vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, prgOneComponentShade); - vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, - vtkgl::PROGRAM_FORMAT_ASCII_ARB, - static_cast(strlen(vtkMitkVolumeTextureMapper3D_OneComponentShadeFP)), - vtkMitkVolumeTextureMapper3D_OneComponentShadeFP); - - vtkgl::GenProgramsARB(1, &prgRGBAShade); - vtkgl::BindProgramARB(vtkgl::FRAGMENT_PROGRAM_ARB, prgRGBAShade); - vtkgl::ProgramStringARB(vtkgl::FRAGMENT_PROGRAM_ARB, - vtkgl::PROGRAM_FORMAT_ASCII_ARB, - static_cast(strlen(vtkMitkVolumeTextureMapper3D_FourDependentShadeFP)), - vtkMitkVolumeTextureMapper3D_FourDependentShadeFP); - } -} - -// ---------------------------------------------------------------------------- -// Print the vtkMitkOpenGLVolumeTextureMapper3D -void vtkMitkOpenGLVolumeTextureMapper3D::PrintSelf(ostream &os, vtkIndent indent) -{ - // vtkOpenGLExtensionManager * extensions = vtkOpenGLExtensionManager::New(); - // extensions->SetRenderWindow(nullptr); // set render window to current render window - - os << indent << "Initialized " << this->Initialized << endl; - /* if ( this->Initialized ) - { - os << indent << "Supports GL_VERSION_1_2:" - << extensions->ExtensionSupported( "GL_VERSION_1_2" ) << endl; - os << indent << "Supports GL_EXT_texture3D:" - << extensions->ExtensionSupported( "GL_EXT_texture3D" ) << endl; - os << indent << "Supports GL_VERSION_1_3:" - << extensions->ExtensionSupported( "GL_VERSION_1_3" ) << endl; - os << indent << "Supports GL_ARB_multitexture: " - << extensions->ExtensionSupported( "GL_ARB_multitexture" ) << endl; - os << indent << "Supports GL_NV_texture_shader2: " - << extensions->ExtensionSupported( "GL_NV_texture_shader2" ) << endl; - os << indent << "Supports GL_NV_register_combiners2: " - << extensions->ExtensionSupported( "GL_NV_register_combiners2" ) - << endl; - os << indent << "Supports GL_ATI_fragment_shader: " - << extensions->ExtensionSupported( "GL_ATI_fragment_shader" ) << endl; - os << indent << "Supports GL_ARB_fragment_program: " - << extensions->ExtensionSupported( "GL_ARB_fragment_program" ) << endl; - os << indent << "Supports GL_ARB_texture_compression: " - << extensions->ExtensionSupported( "GL_ARB_texture_compression" ) - << endl; - os << indent << "Supports GL_VERSION_2_0:" - << extensions->ExtensionSupported( "GL_VERSION_2_0" ) - << endl; - os << indent << "Supports GL_ARB_texture_non_power_of_two:" - << extensions->ExtensionSupported( "GL_ARB_texture_non_power_of_two" ) - << endl; - } - extensions->Delete(); - */ - - if (this->RenderWindow != 0) - { - vtkOpenGLExtensionManager *extensions = - static_cast(this->RenderWindow)->GetExtensionManager(); - - if (this->Initialized) - { - os << indent << "Supports GL_VERSION_1_2:" << extensions->ExtensionSupported("GL_VERSION_1_2") << endl; - os << indent << "Supports GL_EXT_texture3D:" << extensions->ExtensionSupported("GL_EXT_texture3D") << endl; - os << indent << "Supports GL_VERSION_1_3:" << extensions->ExtensionSupported("GL_VERSION_1_3") << endl; - os << indent << "Supports GL_ARB_multitexture: " << extensions->ExtensionSupported("GL_ARB_multitexture") << endl; - os << indent << "Supports GL_NV_texture_shader2: " << extensions->ExtensionSupported("GL_NV_texture_shader2") - << endl; - os << indent - << "Supports GL_NV_register_combiners2: " << extensions->ExtensionSupported("GL_NV_register_combiners2") - << endl; - os << indent << "Supports GL_ATI_fragment_shader: " << extensions->ExtensionSupported("GL_ATI_fragment_shader") - << endl; - os << indent << "Supports GL_ARB_fragment_program: " << extensions->ExtensionSupported("GL_ARB_fragment_program") - << endl; - os << indent - << "Supports GL_ARB_texture_compression: " << extensions->ExtensionSupported("GL_ARB_texture_compression") - << endl; - os << indent << "Supports GL_VERSION_2_0:" << extensions->ExtensionSupported("GL_VERSION_2_0") << endl; - os << indent << "Supports GL_ARB_texture_non_power_of_two:" - << extensions->ExtensionSupported("GL_ARB_texture_non_power_of_two") << endl; - } - } - - this->Superclass::PrintSelf(os, indent); -} diff --git a/Modules/MapperExt/src/vtkMitkVolumeTextureMapper3D.cpp b/Modules/MapperExt/src/vtkMitkVolumeTextureMapper3D.cpp deleted file mode 100644 index dc9e6b306a..0000000000 --- a/Modules/MapperExt/src/vtkMitkVolumeTextureMapper3D.cpp +++ /dev/null @@ -1,705 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "vtkMitkVolumeTextureMapper3D.h" -#include "mitkCommon.h" - -#define GPU_INFO MITK_INFO("mapper.vr") -#define GPU_WARN MITK_WARN("mapper.vr") - -#include "vtkCamera.h" -#include "vtkColorTransferFunction.h" -#include "vtkDataArray.h" -#include "vtkImageData.h" -#include "vtkMath.h" -#include "vtkMatrix4x4.h" -#include "vtkPiecewiseFunction.h" -#include "vtkPointData.h" -#include "vtkRenderer.h" -#include "vtkVolume.h" -#include "vtkVolumeProperty.h" -//#include "vtkVolumeRenderingFactory.h" //VTK6_TODO - -//---------------------------------------------------------------------------- -// Needed when we don't use the vtkStandardNewMacro. -vtkInstantiatorNewMacro(vtkMitkVolumeTextureMapper3D); -//---------------------------------------------------------------------------- - -//----------------------------------------------------------------------------- -vtkMitkVolumeTextureMapper3D::vtkMitkVolumeTextureMapper3D() -{ - // GPU_INFO << "vtkMitkVolumeTextureMapper3D"; - - this->PolygonBuffer = nullptr; - this->IntersectionBuffer = nullptr; - this->NumberOfPolygons = 0; - this->BufferSize = 0; - - // The input used when creating the textures - this->SavedTextureInput = nullptr; - - // The input used when creating the color tables - this->SavedParametersInput = nullptr; - - this->SavedRGBFunction = nullptr; - this->SavedGrayFunction = nullptr; - this->SavedScalarOpacityFunction = nullptr; - this->SavedGradientOpacityFunction = nullptr; - this->SavedColorChannels = 0; - this->SavedSampleDistance = 0; - this->SavedScalarOpacityDistance = 0; - - /* - this->Volume1 = nullptr; - this->Volume2 = nullptr; - this->Volume3 = nullptr; - */ - /* - this->VolumeSize = 0; - this->VolumeComponents = 0; - */ - - this->VolumeSpacing[0] = this->VolumeSpacing[1] = this->VolumeSpacing[2] = 0; - this->VolumeDimensions[0] = 0; - this->VolumeDimensions[1] = 0; - this->VolumeDimensions[2] = 0; - - this->SampleDistance = 1.0; - this->ActualSampleDistance = 1.0; - - this->UseCompressedTexture = false; - this->SupportsNonPowerOfTwoTextures = false; - - // GPU_INFO << "np2: " << (this->SupportsNonPowerOfTwoTextures?1:0); -} - -//----------------------------------------------------------------------------- -vtkMitkVolumeTextureMapper3D::~vtkMitkVolumeTextureMapper3D() -{ - // GPU_INFO << "~vtkMitkVolumeTextureMapper3D"; - - delete[] this->PolygonBuffer; - delete[] this->IntersectionBuffer; - /* - delete [] this->Volume1; - delete [] this->Volume2; - delete [] this->Volume3; - */ -} - -//----------------------------------------------------------------------------- -// vtkMitkVolumeTextureMapper3D *vtkMitkVolumeTextureMapper3D::New() //VTK6_TODO -//{ -// //GPU_INFO << "New"; - -// // First try to create the object from the vtkObjectFactory -// vtkObject* ret = -// vtkVolumeRenderingFactory::CreateInstance("vtkMitkVolumeTextureMapper3D"); -// return static_cast(ret); -//} - -//----------------------------------------------------------------------------- -void vtkMitkVolumeTextureMapper3D::ComputePolygons(vtkRenderer *ren, vtkVolume *vol, double inBounds[6]) -{ - // GPU_INFO << "ComputePolygons"; - - // Get the camera position and focal point - double focalPoint[4], position[4]; - double plane[4]; - vtkCamera *camera = ren->GetActiveCamera(); - - camera->GetPosition(position); - camera->GetFocalPoint(focalPoint); - - position[3] = 1.0; - focalPoint[3] = 1.0; - - // Pass the focal point and position through the inverse of the - // volume's matrix to map back into the data coordinates. We - // are going to compute these polygons in the coordinate system - // of the input data - this is easiest since this data must be - // axis aligned. Then we'll use OpenGL to transform these polygons - // into the world coordinate system through the use of the - // volume's matrix. - vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); - vol->GetMatrix(matrix); - matrix->Invert(); - matrix->MultiplyPoint(position, position); - matrix->MultiplyPoint(focalPoint, focalPoint); - matrix->Delete(); - - if (position[3]) - { - position[0] /= position[3]; - position[1] /= position[3]; - position[2] /= position[3]; - } - - if (focalPoint[3]) - { - focalPoint[0] /= focalPoint[3]; - focalPoint[1] /= focalPoint[3]; - focalPoint[2] /= focalPoint[3]; - } - - // Create a plane equation using the direction and position of the camera - plane[0] = focalPoint[0] - position[0]; - plane[1] = focalPoint[1] - position[1]; - plane[2] = focalPoint[2] - position[2]; - - vtkMath::Normalize(plane); - - plane[3] = -(plane[0] * position[0] + plane[1] * position[1] + plane[2] * position[2]); - - // Find the min and max distances of the boundary points of the volume - double minDistance = VTK_DOUBLE_MAX; - double maxDistance = VTK_DOUBLE_MIN; - - // The inBounds parameter is the bounds we are using for clipping the - // texture planes against. First we need to clip these against the bounds - // of the volume to make sure they don't exceed it. - double volBounds[6]; - this->GetInput()->GetBounds(volBounds); - - double bounds[6]; - bounds[0] = (inBounds[0] > volBounds[0]) ? (inBounds[0]) : (volBounds[0]); - bounds[1] = (inBounds[1] < volBounds[1]) ? (inBounds[1]) : (volBounds[1]); - bounds[2] = (inBounds[2] > volBounds[2]) ? (inBounds[2]) : (volBounds[2]); - bounds[3] = (inBounds[3] < volBounds[3]) ? (inBounds[3]) : (volBounds[3]); - bounds[4] = (inBounds[4] > volBounds[4]) ? (inBounds[4]) : (volBounds[4]); - bounds[5] = (inBounds[5] < volBounds[5]) ? (inBounds[5]) : (volBounds[5]); - - // Create 8 vertices for the bounding box we are rendering - int i, j, k; - double vertices[8][3]; - - int idx = 0; - - for (k = 0; k < 2; k++) - { - for (j = 0; j < 2; j++) - { - for (i = 0; i < 2; i++) - { - vertices[idx][2] = bounds[4 + k]; - vertices[idx][1] = bounds[2 + j]; - vertices[idx][0] = bounds[i]; - - double d = plane[0] * vertices[idx][0] + plane[1] * vertices[idx][1] + plane[2] * vertices[idx][2] + plane[3]; - - idx++; - - // Keep track of closest and farthest point - minDistance = (d < minDistance) ? (d) : (minDistance); - maxDistance = (d > maxDistance) ? (d) : (maxDistance); - } - } - } - - int dim[6]; - this->GetVolumeDimensions(dim); - - float tCoordOffset[3], tCoordScale[3]; - - tCoordOffset[0] = 0.5 / dim[0]; - tCoordOffset[1] = 0.5 / dim[1]; - tCoordOffset[2] = 0.5 / dim[2]; - - tCoordScale[0] = (dim[0] - 1) / static_cast(dim[0]); - tCoordScale[1] = (dim[1] - 1) / static_cast(dim[1]); - tCoordScale[2] = (dim[2] - 1) / static_cast(dim[2]); - - float spacing[3]; - this->GetVolumeSpacing(spacing); - - double offset = 0.333 * 0.5 * (spacing[0] + spacing[1] + spacing[2]); - - minDistance += 0.1 * offset; - maxDistance -= 0.1 * offset; - - minDistance = (minDistance < offset) ? (offset) : (minDistance); - - double stepSize = this->ActualSampleDistance; - - // Determine the number of polygons - int numPolys = static_cast((maxDistance - minDistance) / static_cast(stepSize)); - - // Check if we have space, free old space only if it is too small - if (this->BufferSize < numPolys) - { - delete[] this->PolygonBuffer; - delete[] this->IntersectionBuffer; - - this->BufferSize = numPolys; - - this->PolygonBuffer = new float[36 * this->BufferSize]; - this->IntersectionBuffer = new float[12 * this->BufferSize]; - } - - this->NumberOfPolygons = numPolys; - - // Compute the intersection points for each edge of the volume - int lines[12][2] = {{0, 1}, {1, 3}, {2, 3}, {0, 2}, {4, 5}, {5, 7}, {6, 7}, {4, 6}, {0, 4}, {1, 5}, {3, 7}, {2, 6}}; - - float *iptr, *pptr; - - for (i = 0; i < 12; i++) - { - double line[3]; - - line[0] = vertices[lines[i][1]][0] - vertices[lines[i][0]][0]; - line[1] = vertices[lines[i][1]][1] - vertices[lines[i][0]][1]; - line[2] = vertices[lines[i][1]][2] - vertices[lines[i][0]][2]; - - double d = maxDistance; - - iptr = this->IntersectionBuffer + i; - - double planeDotLineOrigin = vtkMath::Dot(plane, vertices[lines[i][0]]); - double planeDotLine = vtkMath::Dot(plane, line); - - double t, increment; - - if (planeDotLine != 0.0) - { - t = (d - planeDotLineOrigin - plane[3]) / planeDotLine; - increment = -stepSize / planeDotLine; - } - else - { - t = -1.0; - increment = 0.0; - } - - for (j = 0; j < numPolys; j++) - { - *iptr = (t > 0.0 && t < 1.0) ? (t) : (-1.0); - - t += increment; - iptr += 12; - } - } - - // Compute the polygons by determining which edges were intersected - int neighborLines[12][6] = {{1, 2, 3, 4, 8, 9}, - {0, 2, 3, 5, 9, 10}, - {0, 1, 3, 6, 10, 11}, - {0, 1, 2, 7, 8, 11}, - {0, 5, 6, 7, 8, 9}, - {1, 4, 6, 7, 9, 10}, - {2, 4, 5, 7, 10, 11}, - {3, 4, 5, 6, 8, 11}, - {0, 3, 4, 7, 9, 11}, - {0, 1, 4, 5, 8, 10}, - {1, 2, 5, 6, 9, 11}, - {2, 3, 6, 7, 8, 10}}; - - float tCoord[12][4] = {{0, 0, 0, 0}, - {1, 0, 0, 1}, - {0, 1, 0, 0}, - {0, 0, 0, 1}, - {0, 0, 1, 0}, - {1, 0, 1, 1}, - {0, 1, 1, 0}, - {0, 0, 1, 1}, - {0, 0, 0, 2}, - {1, 0, 0, 2}, - {1, 1, 0, 2}, - {0, 1, 0, 2}}; - - double low[3]; - double high[3]; - - low[0] = (bounds[0] - volBounds[0]) / (volBounds[1] - volBounds[0]); - high[0] = (bounds[1] - volBounds[0]) / (volBounds[1] - volBounds[0]); - low[1] = (bounds[2] - volBounds[2]) / (volBounds[3] - volBounds[2]); - high[1] = (bounds[3] - volBounds[2]) / (volBounds[3] - volBounds[2]); - low[2] = (bounds[4] - volBounds[4]) / (volBounds[5] - volBounds[4]); - high[2] = (bounds[5] - volBounds[4]) / (volBounds[5] - volBounds[4]); - - for (i = 0; i < 12; i++) - { - tCoord[i][0] = (tCoord[i][0]) ? (high[0]) : (low[0]); - tCoord[i][1] = (tCoord[i][1]) ? (high[1]) : (low[1]); - tCoord[i][2] = (tCoord[i][2]) ? (high[2]) : (low[2]); - } - - iptr = this->IntersectionBuffer; - pptr = this->PolygonBuffer; - - for (i = 0; i < numPolys; i++) - { - // Look for a starting point - int start = 0; - - while (start < 12 && iptr[start] == -1.0) - { - start++; - } - - if (start == 12) - { - pptr[0] = -1.0; - } - else - { - int current = start; - int previous = -1; - int errFlag = 0; - - idx = 0; - - while (idx < 6 && !errFlag && (idx == 0 || current != start)) - { - double t = iptr[current]; - - *(pptr + idx * 6) = tCoord[current][0] * tCoordScale[0] + tCoordOffset[0]; - *(pptr + idx * 6 + 1) = tCoord[current][1] * tCoordScale[1] + tCoordOffset[1]; - *(pptr + idx * 6 + 2) = tCoord[current][2] * tCoordScale[2] + tCoordOffset[2]; - - int coord = static_cast(tCoord[current][3]); - *(pptr + idx * 6 + coord) = - (low[coord] + t * (high[coord] - low[coord])) * tCoordScale[coord] + tCoordOffset[coord]; - - *(pptr + idx * 6 + 3) = static_cast( - vertices[lines[current][0]][0] + t * (vertices[lines[current][1]][0] - vertices[lines[current][0]][0])); - - *(pptr + idx * 6 + 4) = static_cast( - vertices[lines[current][0]][1] + t * (vertices[lines[current][1]][1] - vertices[lines[current][0]][1])); - - *(pptr + idx * 6 + 5) = static_cast( - vertices[lines[current][0]][2] + t * (vertices[lines[current][1]][2] - vertices[lines[current][0]][2])); - - idx++; - - j = 0; - - while (j < 6 && (*(this->IntersectionBuffer + i * 12 + neighborLines[current][j]) < 0 || - neighborLines[current][j] == previous)) - { - j++; - } - - if (j >= 6) - { - errFlag = 1; - } - else - { - previous = current; - current = neighborLines[current][j]; - } - } - - if (idx < 6) - { - *(pptr + idx * 6) = -1; - } - } - - iptr += 12; - pptr += 36; - } -} - -void vtkMitkVolumeTextureMapper3D::UpdateMTime() -{ - this->SavedTextureMTime.Modified(); -} - -//----------------------------------------------------------------------------- -int vtkMitkVolumeTextureMapper3D::UpdateColorLookup(vtkVolume *vol) -{ - // GPU_INFO << "UpdateColorLookup"; - - int needToUpdate = 0; - - // Get the image data - vtkImageData *input = this->GetInput(); - Update(); - - // Has the volume changed in some way? - if (this->SavedParametersInput != input || this->SavedParametersMTime.GetMTime() < input->GetMTime()) - { - needToUpdate = 1; - } - - // What sample distance are we going to use for rendering? If we - // have to render quickly according to our allocated render time, - // don't necessary obey the sample distance requested by the user. - // Instead set the sample distance to the average spacing. - this->ActualSampleDistance = this->SampleDistance; - if (vol->GetAllocatedRenderTime() < 1.0) - { - float spacing[3]; - this->GetVolumeSpacing(spacing); - this->ActualSampleDistance = - 0.333 * (static_cast(spacing[0]) + static_cast(spacing[1]) + static_cast(spacing[2])); - } - - // How many components? - int components = input->GetNumberOfScalarComponents(); - - // Has the sample distance changed? - if (this->SavedSampleDistance != this->ActualSampleDistance) - { - needToUpdate = 1; - } - - vtkColorTransferFunction *rgbFunc = nullptr; - vtkPiecewiseFunction *grayFunc = nullptr; - - // How many color channels for this component? - int colorChannels = vol->GetProperty()->GetColorChannels(0); - - if (components < 3) - { - // Has the number of color channels changed? - if (this->SavedColorChannels != colorChannels) - { - needToUpdate = 1; - } - - // Has the color transfer function changed in some way, - // and we are using it? - if (colorChannels == 3) - { - rgbFunc = vol->GetProperty()->GetRGBTransferFunction(0); - if (this->SavedRGBFunction != rgbFunc || this->SavedParametersMTime.GetMTime() < rgbFunc->GetMTime()) - { - needToUpdate = 1; - } - } - - // Has the gray transfer function changed in some way, - // and we are using it? - if (colorChannels == 1) - { - grayFunc = vol->GetProperty()->GetGrayTransferFunction(0); - if (this->SavedGrayFunction != grayFunc || this->SavedParametersMTime.GetMTime() < grayFunc->GetMTime()) - { - needToUpdate = 1; - } - } - } - - // Has the scalar opacity transfer function changed in some way? - vtkPiecewiseFunction *scalarOpacityFunc = vol->GetProperty()->GetScalarOpacity(0); - if (this->SavedScalarOpacityFunction != scalarOpacityFunc || - this->SavedParametersMTime.GetMTime() < scalarOpacityFunc->GetMTime()) - { - needToUpdate = 1; - } - - // Has the gradient opacity transfer function changed in some way? - vtkPiecewiseFunction *gradientOpacityFunc = vol->GetProperty()->GetGradientOpacity(0); - if (this->SavedGradientOpacityFunction != gradientOpacityFunc || - this->SavedParametersMTime.GetMTime() < gradientOpacityFunc->GetMTime()) - { - needToUpdate = 1; - } - - double scalarOpacityDistance = vol->GetProperty()->GetScalarOpacityUnitDistance(0); - if (this->SavedScalarOpacityDistance != scalarOpacityDistance) - { - needToUpdate = 1; - } - - // If we have not found any need to update, return now - if (!needToUpdate) - { - return 0; - } - - this->SavedRGBFunction = rgbFunc; - this->SavedGrayFunction = grayFunc; - this->SavedScalarOpacityFunction = scalarOpacityFunc; - this->SavedGradientOpacityFunction = gradientOpacityFunc; - this->SavedColorChannels = colorChannels; - this->SavedSampleDistance = this->ActualSampleDistance; - this->SavedScalarOpacityDistance = scalarOpacityDistance; - this->SavedParametersInput = input; - - this->SavedParametersMTime.Modified(); - - // Find the scalar range - double scalarRange[2]; - input->GetPointData()->GetScalars()->GetRange(scalarRange, components - 1); - - int arraySizeNeeded = this->ColorTableSize; - - if (components < 3) - { - // Sample the transfer functions between the min and max. - if (colorChannels == 1) - { - grayFunc->GetTable(scalarRange[0], scalarRange[1], arraySizeNeeded, this->TempArray1); - } - else - { - rgbFunc->GetTable(scalarRange[0], scalarRange[1], arraySizeNeeded, this->TempArray1); - } - } - - scalarOpacityFunc->GetTable(scalarRange[0], scalarRange[1], arraySizeNeeded, this->TempArray2); - - float goArray[256]; - gradientOpacityFunc->GetTable(0, (scalarRange[1] - scalarRange[0]) * 0.25, 256, goArray); - - // Correct the opacity array for the spacing between the planes. - int i; - - float *fptr2 = this->TempArray2; - double factor = this->ActualSampleDistance / scalarOpacityDistance; - for (i = 0; i < arraySizeNeeded; i++) - { - if (*fptr2 > 0.0001) - { - *fptr2 = 1.0 - pow(static_cast(1.0 - (*fptr2)), factor); - } - fptr2++; - } - - int goLoop; - unsigned char *ptr, *rgbptr, *aptr; - float *fptr1; - - switch (components) - { - case 1: - // Move the two temp float arrays into one RGBA unsigned char array - ptr = this->ColorLookup; - for (goLoop = 0; goLoop < 256; goLoop++) - { - fptr1 = this->TempArray1; - fptr2 = this->TempArray2; - if (colorChannels == 1) - { - for (i = 0; i < arraySizeNeeded; i++) - { - *(ptr++) = static_cast(*(fptr1)*255.0 + 0.5); - *(ptr++) = static_cast(*(fptr1)*255.0 + 0.5); - *(ptr++) = static_cast(*(fptr1++) * 255.0 + 0.5); - *(ptr++) = static_cast(*(fptr2++) * goArray[goLoop] * 255.0 + 0.5); - } - } - else - { - for (i = 0; i < arraySizeNeeded; i++) - { - *(ptr++) = static_cast(*(fptr1++) * 255.0 + 0.5); - *(ptr++) = static_cast(*(fptr1++) * 255.0 + 0.5); - *(ptr++) = static_cast(*(fptr1++) * 255.0 + 0.5); - *(ptr++) = static_cast(*(fptr2++) * goArray[goLoop] * 255.0 + 0.5); - } - } - - for (; i < 256; i++) - { - *(ptr++) = 0; - *(ptr++) = 0; - *(ptr++) = 0; - *(ptr++) = 0; - } - } - break; - - case 2: - // Move the two temp float arrays into one RGB unsigned char array and - // one alpha array. - rgbptr = this->ColorLookup; - aptr = this->AlphaLookup; - - if (colorChannels == 1) - { - for (i = 0; i < arraySizeNeeded; i++) - { - fptr1 = this->TempArray1; - fptr2 = this->TempArray2; - for (goLoop = 0; goLoop < 256; goLoop++) - { - *(rgbptr++) = static_cast(*(fptr1)*255.0 + 0.5); - *(rgbptr++) = static_cast(*(fptr1)*255.0 + 0.5); - *(rgbptr++) = static_cast(*(fptr1++) * 255.0 + 0.5); - *(aptr++) = static_cast(*(fptr2++) * goArray[goLoop] * 255.0 + 0.5); - } - } - } - else - { - fptr1 = this->TempArray1; - fptr2 = this->TempArray2; - for (i = 0; i < arraySizeNeeded; i++) - { - for (goLoop = 0; goLoop < 256; goLoop++) - { - *(rgbptr++) = static_cast(*(fptr1)*255.0 + 0.5); - *(rgbptr++) = static_cast(*(fptr1 + 1) * 255.0 + 0.5); - *(rgbptr++) = static_cast(*(fptr1 + 2) * 255.0 + 0.5); - *(aptr++) = static_cast(*(fptr2)*goArray[goLoop] * 255.0 + 0.5); - } - fptr1 += 3; - fptr2++; - } - } - - for (; i < 256; i++) - { - for (goLoop = 0; goLoop < 256; goLoop++) - { - *(rgbptr++) = 0; - *(rgbptr++) = 0; - *(rgbptr++) = 0; - *(aptr++) = 0; - } - } - break; - - case 3: - case 4: - // Move the two temp float arrays into one alpha array - aptr = this->AlphaLookup; - - for (goLoop = 0; goLoop < 256; goLoop++) - { - fptr2 = this->TempArray2; - for (i = 0; i < arraySizeNeeded; i++) - { - *(aptr++) = static_cast(*(fptr2++) * goArray[goLoop] * 255.0 + 0.5); - } - for (; i < 256; i++) - { - *(aptr++) = 0; - } - } - - break; - } - return 1; -} - -//----------------------------------------------------------------------------- -// Print the vtkMitkVolumeTextureMapper3D -void vtkMitkVolumeTextureMapper3D::PrintSelf(ostream &os, vtkIndent indent) -{ - this->Superclass::PrintSelf(os, indent); - - os << indent << "Sample Distance: " << this->SampleDistance << endl; - os << indent << "NumberOfPolygons: " << this->NumberOfPolygons << endl; - os << indent << "ActualSampleDistance: " << this->ActualSampleDistance << endl; - os << indent << "VolumeDimensions: " << this->VolumeDimensions[0] << " " << this->VolumeDimensions[1] << " " - << this->VolumeDimensions[2] << endl; - os << indent << "VolumeSpacing: " << this->VolumeSpacing[0] << " " << this->VolumeSpacing[1] << " " - << this->VolumeSpacing[2] << endl; - - os << indent << "UseCompressedTexture: " << this->UseCompressedTexture << endl; -} diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.cpp b/Modules/Multilabel/mitkLabelSetIOHelper.cpp index af54833042..9833b4151c 100644 --- a/Modules/Multilabel/mitkLabelSetIOHelper.cpp +++ b/Modules/Multilabel/mitkLabelSetIOHelper.cpp @@ -1,275 +1,280 @@ /*============================================================================ 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 "mitkLabelSetIOHelper.h" #include "mitkLabelSetImage.h" #include #include namespace { std::string EnsureExtension(const std::string& filename) { const std::string extension = ".lsetp"; if (filename.size() < extension.size() || std::string::npos == filename.find(extension, filename.size() - extension.size())) return filename + extension; return filename; } } bool mitk::LabelSetIOHelper::SaveLabelSetImagePreset(const std::string &presetFilename, - mitk::LabelSetImage::Pointer &inputImage) + const mitk::LabelSetImage *inputImage) { const auto filename = EnsureExtension(presetFilename); tinyxml2::XMLDocument xmlDocument; xmlDocument.InsertEndChild(xmlDocument.NewDeclaration()); auto *rootElement = xmlDocument.NewElement("LabelSetImagePreset"); rootElement->SetAttribute("layers", inputImage->GetNumberOfLayers()); xmlDocument.InsertEndChild(rootElement); for (unsigned int layerIndex = 0; layerIndex < inputImage->GetNumberOfLayers(); layerIndex++) { auto *layerElement = xmlDocument.NewElement("Layer"); layerElement->SetAttribute("index", layerIndex); layerElement->SetAttribute("labels", inputImage->GetNumberOfLabels(layerIndex)); rootElement->InsertEndChild(layerElement); for (unsigned int labelIndex = 0; labelIndex < inputImage->GetNumberOfLabels(layerIndex); labelIndex++) layerElement->InsertEndChild(LabelSetIOHelper::GetLabelAsXMLElement(xmlDocument, inputImage->GetLabel(labelIndex, layerIndex))); } return tinyxml2::XML_SUCCESS == xmlDocument.SaveFile(filename.c_str()); } -void mitk::LabelSetIOHelper::LoadLabelSetImagePreset(const std::string &presetFilename, - mitk::LabelSetImage::Pointer &inputImage) +bool mitk::LabelSetIOHelper::LoadLabelSetImagePreset(const std::string &presetFilename, + mitk::LabelSetImage *inputImage) { - if (inputImage.IsNull()) - return; + if (nullptr == inputImage) + return false; const auto filename = EnsureExtension(presetFilename); tinyxml2::XMLDocument xmlDocument; - if(tinyxml2::XML_SUCCESS != xmlDocument.LoadFile(filename.c_str())) - return; + if (tinyxml2::XML_SUCCESS != xmlDocument.LoadFile(filename.c_str())) + { + MITK_WARN << "Label set preset file \"" << filename << "\" does not exist or cannot be opened"; + return false; + } auto *rootElement = xmlDocument.FirstChildElement("LabelSetImagePreset"); if (nullptr == rootElement) { - MITK_WARN << "Not a valid LabelSet preset"; - return; + MITK_WARN << "Not a valid Label set preset"; + return false; } auto activeLayerBackup = inputImage->GetActiveLayer(); int numberOfLayers = 0; rootElement->QueryIntAttribute("layers", &numberOfLayers); auto* layerElement = rootElement->FirstChildElement("Layer"); if (nullptr == layerElement) { - MITK_WARN << "LabelSet preset does not contain any layers"; - return; + MITK_WARN << "Label set preset does not contain any layers"; + return false; } for (int layerIndex = 0; layerIndex < numberOfLayers; layerIndex++) { int numberOfLabels = 0; layerElement->QueryIntAttribute("labels", &numberOfLabels); if (nullptr == inputImage->GetLabelSet(layerIndex)) { inputImage->AddLayer(); } else { inputImage->SetActiveLayer(layerIndex); } auto *labelElement = layerElement->FirstChildElement("Label"); if (nullptr == labelElement) continue; for (int labelIndex = 0; labelIndex < numberOfLabels; labelIndex++) { auto label = mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(labelElement); const auto labelValue = label->GetValue(); if (0 != labelValue) { auto* labelSet = inputImage->GetLabelSet(layerIndex); auto* alreadyExistingLabel = labelSet->GetLabel(labelValue); if (nullptr != alreadyExistingLabel) { // Override existing label with label from preset alreadyExistingLabel->ConcatenatePropertyList(label); labelSet->UpdateLookupTable(labelValue); } else { labelSet->AddLabel(label); } } labelElement = labelElement->NextSiblingElement("Label"); if (nullptr == labelElement) continue; } layerElement = layerElement->NextSiblingElement("Layer"); if (nullptr == layerElement) continue; } inputImage->SetActiveLayer(activeLayerBackup); + + return true; } tinyxml2::XMLElement *mitk::LabelSetIOHelper::GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, Label *label) { auto *labelElem = doc.NewElement("Label"); if (nullptr != label) { // add XML contents const PropertyList::PropertyMap* propmap = label->GetMap(); for (auto iter = propmap->begin(); iter != propmap->end(); ++iter) { std::string key = iter->first; const BaseProperty* property = iter->second; auto* element = PropertyToXMLElement(doc, key, property); if (element) labelElem->InsertEndChild(element); } } return labelElem; } mitk::Label::Pointer mitk::LabelSetIOHelper::LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem) { // reread auto *propElem = labelElem->FirstChildElement("property"); std::string name; mitk::BaseProperty::Pointer prop; mitk::Label::Pointer label = mitk::Label::New(); while (propElem) { LabelSetIOHelper::PropertyFromXMLElement(name, prop, propElem); label->SetProperty(name, prop); propElem = propElem->NextSiblingElement("property"); } return label.GetPointer(); } tinyxml2::XMLElement *mitk::LabelSetIOHelper::PropertyToXMLElement(tinyxml2::XMLDocument &doc, const std::string &key, const BaseProperty *property) { auto *keyelement = doc.NewElement("property"); keyelement->SetAttribute("key", key.c_str()); keyelement->SetAttribute("type", property->GetNameOfClass()); // construct name of serializer class std::string serializername(property->GetNameOfClass()); serializername += "Serializer"; std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (allSerializers.size() < 1) MITK_ERROR << "No serializer found for " << property->GetNameOfClass() << ". Skipping object"; if (allSerializers.size() > 1) MITK_WARN << "Multiple serializers found for " << property->GetNameOfClass() << "Using arbitrarily the first one."; for (auto iter = allSerializers.begin(); iter != allSerializers.end(); ++iter) { if (auto *serializer = dynamic_cast(iter->GetPointer())) { serializer->SetProperty(property); try { auto *valueelement = serializer->Serialize(doc); if (valueelement) keyelement->InsertEndChild(valueelement); } catch (std::exception &e) { MITK_ERROR << "Serializer " << serializer->GetNameOfClass() << " failed: " << e.what(); } break; } } return keyelement; } bool mitk::LabelSetIOHelper::PropertyFromXMLElement(std::string &key, mitk::BaseProperty::Pointer &prop, const tinyxml2::XMLElement *elem) { const char* typeC = elem->Attribute("type"); std::string type = nullptr != typeC ? typeC : ""; const char* keyC = elem->Attribute("key"); key = nullptr != keyC ? keyC : ""; // construct name of serializer class std::string serializername(type); serializername += "Serializer"; std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); if (allSerializers.size() < 1) MITK_ERROR << "No serializer found for " << type << ". Skipping object"; if (allSerializers.size() > 1) MITK_WARN << "Multiple deserializers found for " << type << "Using arbitrarily the first one."; for (auto iter = allSerializers.begin(); iter != allSerializers.end(); ++iter) { if (auto *serializer = dynamic_cast(iter->GetPointer())) { try { prop = serializer->Deserialize(elem->FirstChildElement()); } catch (std::exception &e) { MITK_ERROR << "Deserializer " << serializer->GetNameOfClass() << " failed: " << e.what(); return false; } break; } } if (prop.IsNull()) return false; return true; } diff --git a/Modules/Multilabel/mitkLabelSetIOHelper.h b/Modules/Multilabel/mitkLabelSetIOHelper.h index a43c16c686..905dcc6147 100644 --- a/Modules/Multilabel/mitkLabelSetIOHelper.h +++ b/Modules/Multilabel/mitkLabelSetIOHelper.h @@ -1,97 +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. ============================================================================*/ #ifndef __mitkLabelSetIOHelper_h #define __mitkLabelSetIOHelper_h #include #include namespace tinyxml2 { class XMLDocument; class XMLElement; } namespace mitk { class BaseProperty; class LabelSetImage; class Label; /** * @brief The LabelSetIOHelper is a static helper class that supports serialization of mitk::LabelSetImage * * This class provides static functions for converting mitk::Label into XML and also allows the serialization * of mitk::LabelSet as presets */ class MITKMULTILABEL_EXPORT LabelSetIOHelper { public: /** * @brief Saves the mitk::LabelSet configuration of inputImage to presetFilename. * The preset is stored as "*.lsetp" * @param presetFilename the filename including the filesystem path * @param inputImage the input image from which the preset should be generated * @return true if the serialization was successful and false otherwise */ static bool SaveLabelSetImagePreset(const std::string &presetFilename, - itk::SmartPointer &inputImage); + const mitk::LabelSetImage *inputImage); /** * @brief Loads an existing preset for a mitk::LabelSetImage from presetFilename and applies it to inputImage * @param presetFilename the filename of the preset including the filesystem path * @param inputImage the image to which the loaded preset will be applied + * @return true if the deserilization was successful and false otherwise */ - static void LoadLabelSetImagePreset(const std::string &presetFilename, - itk::SmartPointer &inputImage); + static bool LoadLabelSetImagePreset(const std::string &presetFilename, + mitk::LabelSetImage *inputImage); /** * @brief Creates a mitk::Label from an XML element * @param labelElem the xml element from which a mitk::Label will be created * @return the created mitk::Label */ static itk::SmartPointer LoadLabelFromXMLDocument(const tinyxml2::XMLElement *labelElem); /** * @brief Creates an XML element from a mitk::Label * @param doc * @param label the mitk::Label from which the xml element will be created * @return the created XML element */ static tinyxml2::XMLElement *GetLabelAsXMLElement(tinyxml2::XMLDocument &doc, Label *label); /** * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts the label's properties into * XML * @param doc * @param key the property's key which will be used in the XML element * @param property the mitk::BaseProperty that should be converted * @return the created XML element */ static tinyxml2::XMLElement *PropertyToXMLElement(tinyxml2::XMLDocument& doc, const std::string &key, const BaseProperty *property); /** * @brief Since a mitk::Label is basically a mitk::PropertyList this function coverts a XML element into a property * @param key the property's key * @param prop the mitk::BaseProperty that will be created * @param elem the XML elem from which the property will be created * @return true if the conversion was successful and false otherwise */ static bool PropertyFromXMLElement(std::string &key, itk::SmartPointer &prop, const tinyxml2::XMLElement *elem); private: LabelSetIOHelper(); }; } #endif // __mitkLabelSetIOHelper_h diff --git a/Modules/Multilabel/mitkLabelSetImageHelper.cpp b/Modules/Multilabel/mitkLabelSetImageHelper.cpp index 8b4eaed591..956d06a55f 100644 --- a/Modules/Multilabel/mitkLabelSetImageHelper.cpp +++ b/Modules/Multilabel/mitkLabelSetImageHelper.cpp @@ -1,77 +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. ============================================================================*/ #include #include #include #include +#include +#include +#include + +namespace +{ + template + std::array QuantizeColor(const T* color) + { + return { + static_cast(std::round(color[0] * 255)), + static_cast(std::round(color[1] * 255)), + static_cast(std::round(color[2] * 255)) }; + } + + mitk::Color FromLookupTableColor(const double* lookupTableColor) + { + mitk::Color color; + color.Set( + static_cast(lookupTableColor[0]), + static_cast(lookupTableColor[1]), + static_cast(lookupTableColor[2])); + return color; + } +} + mitk::DataNode::Pointer mitk::LabelSetImageHelper::CreateNewSegmentationNode(const DataNode* referenceNode, const Image* initialSegmentationImage, const std::string& segmentationName) { std::string newSegmentationName = segmentationName; if (newSegmentationName.empty()) { newSegmentationName = referenceNode->GetName(); newSegmentationName.append("-labels"); } if (nullptr == initialSegmentationImage) { return nullptr; } auto newLabelSetImage = mitk::LabelSetImage::New(); try { newLabelSetImage->Initialize(initialSegmentationImage); } catch (mitk::Exception &e) { mitkReThrow(e) << "Could not initialize new label set image."; return nullptr; } auto newSegmentationNode = mitk::DataNode::New(); newSegmentationNode->SetData(newLabelSetImage); newSegmentationNode->SetName(newSegmentationName); // set additional image information newLabelSetImage->GetExteriorLabel()->SetProperty("name.parent", mitk::StringProperty::New(referenceNode->GetName())); newLabelSetImage->GetExteriorLabel()->SetProperty("name.image", mitk::StringProperty::New(newSegmentationName)); // initialize "showVolume"-property to false to prevent recalculating the volume while working on the segmentation newSegmentationNode->SetProperty("showVolume", mitk::BoolProperty::New(false)); return newSegmentationNode; } mitk::Label::Pointer mitk::LabelSetImageHelper::CreateNewLabel(const LabelSetImage* labelSetImage) { - int numberOfLabels = labelSetImage->GetActiveLabelSet()->GetNumberOfLabels(); + if (nullptr == labelSetImage) + return nullptr; + + const std::regex genericLabelNameRegEx("New label ([1-9][0-9]*)"); + int maxGenericLabelNumber = 0; + + std::vector> colorsInUse; + + const auto numLabelSets = labelSetImage->GetNumberOfLayers(); + + for (std::remove_const_t i = 0; i < numLabelSets; ++i) + { + auto labelSet = labelSetImage->GetLabelSet(i); + auto labelEndIter = labelSet->IteratorConstEnd(); - std::string labelName = "New label " + std::to_string(numberOfLabels); + for (auto labelIter = labelSet->IteratorConstBegin(); labelIter != labelEndIter; ++labelIter) + { + auto label = labelIter->second; + auto labelName = label->GetName(); + std::smatch match; - mitk::LookupTable::Pointer lookupTable = mitk::LookupTable::New(); + if (std::regex_match(labelName, match, genericLabelNameRegEx)) + maxGenericLabelNumber = std::max(maxGenericLabelNumber, std::stoi(match[1].str())); + + const auto quantizedLabelColor = QuantizeColor(label->GetColor().data()); + + if (std::find(colorsInUse.begin(), colorsInUse.end(), quantizedLabelColor) == std::end(colorsInUse)) + colorsInUse.push_back(quantizedLabelColor); + } + } + + auto newLabel = mitk::Label::New(); + newLabel->SetName("New label " + std::to_string(maxGenericLabelNumber + 1)); + + auto lookupTable = mitk::LookupTable::New(); lookupTable->SetType(mitk::LookupTable::LookupTableType::MULTILABEL); - double rgb[3]; - lookupTable->GetColor(numberOfLabels, rgb); - mitk::Color labelColor; - labelColor.Set(static_cast(rgb[0]), static_cast(rgb[1]), static_cast(rgb[2])); - - mitk::Label::Pointer newLabel = mitk::Label::New(); - newLabel->SetName(labelName); - newLabel->SetColor(labelColor); + + std::array lookupTableColor; + const int maxTries = 25; + bool newColorFound = false; + + for (int i = 0; i < maxTries; ++i) + { + lookupTable->GetColor(i, lookupTableColor.data()); + + auto quantizedLookupTableColor = QuantizeColor(lookupTableColor.data()); + + if (std::find(colorsInUse.begin(), colorsInUse.end(), quantizedLookupTableColor) == std::end(colorsInUse)) + { + newLabel->SetColor(FromLookupTableColor(lookupTableColor.data())); + newColorFound = true; + break; + } + } + + if (!newColorFound) + { + lookupTable->GetColor(labelSetImage->GetTotalNumberOfLabels(), lookupTableColor.data()); + newLabel->SetColor(FromLookupTableColor(lookupTableColor.data())); + } return newLabel; } diff --git a/Modules/Multilabel/mitkLabelSetImageHelper.h b/Modules/Multilabel/mitkLabelSetImageHelper.h index 44c6fc4187..ddcdfa39ab 100644 --- a/Modules/Multilabel/mitkLabelSetImageHelper.h +++ b/Modules/Multilabel/mitkLabelSetImageHelper.h @@ -1,60 +1,59 @@ /*============================================================================ 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 MITKLABELSETIMAGEHELPER_H #define MITKLABELSETIMAGEHELPER_H #include #include #include namespace mitk { /** * */ namespace LabelSetImageHelper { /** * @brief This function creates and returns a new data node containing the given image as data. * The segmentation node is named according to the given reference data node. * Some properties are set to automatically link the segmentation node with its parent node. * * @param referenceNode The reference node from which the name of the new segmentation node * is derived. * @param initialSegmentationImage The segmentation image that is used to initialize the label set image. * @param segmentationName An optional name for the new segmentation node. * * @return The new segmentation node as a data node pointer. */ MITKMULTILABEL_EXPORT mitk::DataNode::Pointer CreateNewSegmentationNode(const DataNode* referenceNode, const Image* initialSegmentationImage = nullptr, const std::string& segmentationName = std::string()); /** - * @brief This function creates and returns a new label. The label is automatically assigned a - * label name, depending on the current number of labels of the active label set of the + * @brief This function creates and returns a new label. The label is automatically assigned an + * unused generic label name, depending on existing label names in all label sets of the * given label set image. - * The color of the label is derived from the MULTILABEL lookup table, depending on the - * number of labels. + * The color of the label is selected from the MULTILABEL lookup table, following the same + * rules of the naming to likely chose a unique color. * - * @param labelSetImage The label set image from which the number of labels of the active label - * set is derived. + * @param labelSetImage The label set image that the new label is added to * - * @return The new labe as a pointer. + * @return The new label. */ MITKMULTILABEL_EXPORT mitk::Label::Pointer CreateNewLabel(const LabelSetImage* labelSetImage); } // namespace LabelSetImageHelper } // namespace mitk #endif // MITKLABELSETIMAGEHELPER_H diff --git a/Modules/SceneSerializationBase/src/mitkEnumerationSubclassesSerializer.cpp b/Modules/SceneSerializationBase/src/mitkEnumerationSubclassesSerializer.cpp index 700de1d768..ff01f7bc91 100644 --- a/Modules/SceneSerializationBase/src/mitkEnumerationSubclassesSerializer.cpp +++ b/Modules/SceneSerializationBase/src/mitkEnumerationSubclassesSerializer.cpp @@ -1,70 +1,68 @@ /*============================================================================ 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 mitkEnumerationSubclassesSerializer_h_included #define mitkEnumerationSubclassesSerializer_h_included #include "mitkEnumerationPropertySerializer.h" #include "mitkModalityProperty.h" #include "mitkPlaneOrientationProperty.h" #include "mitkPointSetShapeProperty.h" #include "mitkRenderingModeProperty.h" #include "mitkVtkInterpolationProperty.h" #include "mitkVtkRepresentationProperty.h" #include "mitkVtkResliceInterpolationProperty.h" #include "mitkVtkScalarModeProperty.h" -#include "mitkVtkVolumeRenderingProperty.h" #include #define MITK_REGISTER_ENUM_SUB_SERIALIZER(classname) \ \ namespace mitk \ { \ class classname##Serializer : public EnumerationPropertySerializer \ { \ public: \ mitkClassMacro(classname##Serializer, EnumerationPropertySerializer) \ itkFactorylessNewMacro(Self) \ itkCloneMacro(Self) \ \ virtual BaseProperty::Pointer Deserialize(const tinyxml2::XMLElement *element) override \ { \ if (nullptr == element) \ return nullptr; \ \ classname::Pointer property = classname::New(); \ property->SetValue(element->Attribute("value")); \ \ return property.GetPointer(); \ } \ \ protected: \ classname##Serializer() {} \ virtual ~classname##Serializer() {} \ }; \ } \ \ MITK_REGISTER_SERIALIZER(classname##Serializer); MITK_REGISTER_ENUM_SUB_SERIALIZER(PlaneOrientationProperty); MITK_REGISTER_ENUM_SUB_SERIALIZER(VtkInterpolationProperty); MITK_REGISTER_ENUM_SUB_SERIALIZER(VtkRepresentationProperty); MITK_REGISTER_ENUM_SUB_SERIALIZER(VtkResliceInterpolationProperty); MITK_REGISTER_ENUM_SUB_SERIALIZER(VtkScalarModeProperty); -MITK_REGISTER_ENUM_SUB_SERIALIZER(VtkVolumeRenderingProperty); MITK_REGISTER_ENUM_SUB_SERIALIZER(ModalityProperty); MITK_REGISTER_ENUM_SUB_SERIALIZER(RenderingModeProperty); MITK_REGISTER_ENUM_SUB_SERIALIZER(PointSetShapeProperty); #endif diff --git a/Modules/SceneSerializationBase/test/mitkPropertySerializationTest.cpp b/Modules/SceneSerializationBase/test/mitkPropertySerializationTest.cpp index df7caa32f5..67dd422e65 100644 --- a/Modules/SceneSerializationBase/test/mitkPropertySerializationTest.cpp +++ b/Modules/SceneSerializationBase/test/mitkPropertySerializationTest.cpp @@ -1,202 +1,200 @@ /*============================================================================ 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 "mitkTestingMacros.h" #include "mitkCoreObjectFactory.h" #include "mitkBaseProperty.h" #include "mitkProperties.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include #include "mitkBasePropertySerializer.h" #include "mitkPropertyList.h" #include "mitkPropertyListSerializer.h" #include #include #include #include #include void TestAllProperties(const mitk::PropertyList *propList); /**Documentation * \brief Test for all PropertySerializer classes. * */ int mitkPropertySerializationTest(int /* argc */, char * /*argv*/ []) { MITK_TEST_BEGIN("PropertySerializationTest"); mitk::PropertyListSerializer::Pointer serializer = mitk::PropertyListSerializer::New(); // make sure something from the lib is actually used (registration of // serializers) /* build list of properties that will be serialized and deserialized */ mitk::PropertyList::Pointer propList = mitk::PropertyList::New(); propList->SetProperty("booltrue", mitk::BoolProperty::New(true)); propList->SetProperty("boolfalse", mitk::BoolProperty::New(false)); propList->SetProperty("int", mitk::IntProperty::New(-32)); propList->SetProperty("float", mitk::FloatProperty::New(-31.337)); propList->SetProperty("double", mitk::DoubleProperty::New(-31.337)); propList->SetProperty("string", mitk::StringProperty::New("Hello MITK")); mitk::Point3D p3d; mitk::FillVector3D(p3d, 1.0, 2.2, -3.3); propList->SetProperty("p3d", mitk::Point3dProperty::New(p3d)); mitk::Point3I p3i; mitk::FillVector3D(p3i, 1, 2, -3); propList->SetProperty("p3i", mitk::Point3iProperty::New(p3i)); mitk::Point4D p4d; mitk::FillVector4D(p4d, 1.5, 2.6, -3.7, 4.44); propList->SetProperty("p4d", mitk::Point4dProperty::New(p4d)); mitk::Vector3D v3d; mitk::FillVector3D(v3d, 1.0, 2.2, -3.3); propList->SetProperty("v3d", mitk::Vector3DProperty::New(v3d)); propList->SetProperty("annotation", mitk::AnnotationProperty::New("My Annotation", p3d)); propList->SetProperty("clipping", mitk::ClippingProperty::New(p3d, v3d)); propList->SetProperty("color", mitk::ColorProperty::New(1.0, 0.2, 0.2)); propList->SetProperty("modality", mitk::ModalityProperty::New("Color Doppler")); propList->SetProperty("PlaneOrientationProperty", mitk::PlaneOrientationProperty::New("Arrows in positive direction")); propList->SetProperty("VtkInterpolationProperty", mitk::VtkInterpolationProperty::New("Gouraud")); propList->SetProperty("VtkRepresentationProperty", mitk::VtkRepresentationProperty::New("Surface")); propList->SetProperty("VtkResliceInterpolationProperty", mitk::VtkResliceInterpolationProperty::New("Cubic")); propList->SetProperty("VtkScalarModeProperty", mitk::VtkScalarModeProperty::New("PointFieldData")); - propList->SetProperty("VtkVolumeRenderingProperty", mitk::VtkVolumeRenderingProperty::New("COMPOSITE")); mitk::BoolLookupTable blt; blt.SetTableValue(0, true); blt.SetTableValue(1, false); blt.SetTableValue(2, true); propList->SetProperty("BoolLookupTableProperty", mitk::BoolLookupTableProperty::New(blt)); mitk::FloatLookupTable flt; flt.SetTableValue(0, 3.1); flt.SetTableValue(1, 3.3); flt.SetTableValue(2, 7.0); propList->SetProperty("FloatLookupTableProperty", mitk::FloatLookupTableProperty::New(flt)); mitk::IntLookupTable ilt; ilt.SetTableValue(0, 3); ilt.SetTableValue(1, 2); ilt.SetTableValue(2, 11); propList->SetProperty("IntLookupTableProperty", mitk::IntLookupTableProperty::New(ilt)); mitk::StringLookupTable slt; slt.SetTableValue(0, "Hello"); slt.SetTableValue(1, "MITK"); slt.SetTableValue(2, "world"); propList->SetProperty("StringLookupTableProperty", mitk::StringLookupTableProperty::New(slt)); propList->SetProperty("GroupTagProperty", mitk::GroupTagProperty::New()); propList->SetProperty("LevelWindowProperty", mitk::LevelWindowProperty::New(mitk::LevelWindow(100.0, 50.0))); mitk::LookupTable::Pointer lt = mitk::LookupTable::New(); lt->ChangeOpacityForAll(0.25); lt->ChangeOpacity(17, 0.88); propList->SetProperty("LookupTableProperty", mitk::LookupTableProperty::New(lt)); propList->SetProperty("StringProperty", mitk::StringProperty::New("Oh why, gruel world")); MITK_TEST_CONDITION_REQUIRED(propList->GetMap()->size() > 0, "Initialize PropertyList"); TestAllProperties(propList); /* test default property lists of basedata objects */ // activate the following tests after MaterialProperty is deleted mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(mitk::PointSet::New()); TestAllProperties(node->GetPropertyList()); node->SetData(mitk::Image::New()); TestAllProperties(node->GetPropertyList()); node->SetData(mitk::Surface::New()); TestAllProperties(node->GetPropertyList()); node->SetData(mitk::VtkWidgetRendering::New()); TestAllProperties(node->GetPropertyList()); MITK_TEST_END(); } void TestAllProperties(const mitk::PropertyList *propList) { assert(propList); /* try to serialize each property in the list, then deserialize again and check for equality */ for (auto it = propList->GetMap()->begin(); it != propList->GetMap()->end(); ++it) { const mitk::BaseProperty *prop = it->second; // construct name of serializer class std::string serializername = std::string(prop->GetNameOfClass()) + "Serializer"; std::list allSerializers = itk::ObjectFactoryBase::CreateAllInstance(serializername.c_str()); MITK_TEST_CONDITION(allSerializers.size() > 0, std::string("Creating serializers for ") + serializername); if (allSerializers.size() == 0) { MITK_TEST_OUTPUT(<< "serialization not possible, skipping " << prop->GetNameOfClass()); continue; } if (allSerializers.size() > 1) { MITK_TEST_OUTPUT(<< "Warning: " << allSerializers.size() << " serializers found for " << prop->GetNameOfClass() << "testing only the first one."); } auto *serializer = dynamic_cast(allSerializers.begin()->GetPointer()); MITK_TEST_CONDITION(serializer != nullptr, serializername + std::string(" is valid")); if (serializer != nullptr) { serializer->SetProperty(prop); tinyxml2::XMLDocument doc; tinyxml2::XMLElement *valueelement = nullptr; try { valueelement = serializer->Serialize(doc); } catch (...) { } MITK_TEST_CONDITION(valueelement != nullptr, std::string("Serialize property with ") + serializername); if (valueelement == nullptr) { MITK_TEST_OUTPUT(<< "serialization failed, skipping deserialization"); continue; } mitk::BaseProperty::Pointer deserializedProp = serializer->Deserialize(valueelement); MITK_TEST_CONDITION(deserializedProp.IsNotNull(), "serializer created valid property"); if (deserializedProp.IsNotNull()) { MITK_TEST_CONDITION(*(deserializedProp.GetPointer()) == *prop, "deserialized property equals initial property for type " << prop->GetNameOfClass()); } } else { MITK_TEST_OUTPUT(<< "created serializer object is of class " << allSerializers.begin()->GetPointer()->GetNameOfClass()) } } // for all properties } diff --git a/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.cpp b/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.cpp index 9ae4041a66..ba93a9b4e7 100644 --- a/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.cpp +++ b/Modules/Segmentation/Algorithms/mitkSegmentationObjectFactory.cpp @@ -1,138 +1,123 @@ /*============================================================================ 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 "mitkSegmentationObjectFactory.h" #include "mitkBaseRenderer.h" #include "mitkCoreObjectFactory.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include "mitkContour.h" #include "mitkContourMapper2D.h" #include "mitkContourSetMapper2D.h" #include "mitkContourSetVtkMapper3D.h" #include "mitkContourVtkMapper3D.h" #include mitk::SegmentationObjectFactory::SegmentationObjectFactory() : CoreObjectFactoryBase() { static bool alreadyDone = false; if (!alreadyDone) { MITK_DEBUG << "SegmentationObjectFactory c'tor" << std::endl; CreateFileExtensionsMap(); alreadyDone = true; } } mitk::Mapper::Pointer mitk::SegmentationObjectFactory::CreateMapper(mitk::DataNode *node, MapperSlotId id) { mitk::Mapper::Pointer newMapper = nullptr; mitk::BaseData *data = node->GetData(); if (id == mitk::BaseRenderer::Standard2D) { std::string classname("ContourModel"); if (dynamic_cast(node->GetData()) != nullptr) { newMapper = mitk::ContourMapper2D::New(); newMapper->SetDataNode(node); } else if (dynamic_cast(node->GetData()) != nullptr) { newMapper = mitk::ContourSetMapper2D::New(); newMapper->SetDataNode(node); } } else if (id == mitk::BaseRenderer::Standard3D) { if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::ContourVtkMapper3D::New(); newMapper->SetDataNode(node); } else if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::ContourSetVtkMapper3D::New(); newMapper->SetDataNode(node); } } return newMapper; } -void mitk::SegmentationObjectFactory::SetDefaultProperties(mitk::DataNode *node) +void mitk::SegmentationObjectFactory::SetDefaultProperties(mitk::DataNode *) { - if (node == nullptr) - return; - - mitk::DataNode::Pointer nodePointer = node; - - // mitk::Image::Pointer image = dynamic_cast(node->GetData()); - // if(image.IsNotNull() && image->IsInitialized()) - // { - // mitk::GPUVolumeMapper3D::SetDefaultProperties(node); - // } - // - // if (dynamic_cast(node->GetData())) - // { - // mitk::UnstructuredGridVtkMapper3D::SetDefaultProperties(node); - // } } std::string mitk::SegmentationObjectFactory::GetFileExtensions() { std::string fileExtension; this->CreateFileExtensions(m_FileExtensionsMap, fileExtension); return fileExtension.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::SegmentationObjectFactory::GetFileExtensionsMap() { return m_FileExtensionsMap; } mitk::CoreObjectFactoryBase::MultimapType mitk::SegmentationObjectFactory::GetSaveFileExtensionsMap() { return m_SaveFileExtensionsMap; } void mitk::SegmentationObjectFactory::CreateFileExtensionsMap() { } std::string mitk::SegmentationObjectFactory::GetSaveFileExtensions() { std::string fileExtension; this->CreateFileExtensions(m_SaveFileExtensionsMap, fileExtension); return fileExtension.c_str(); } void mitk::SegmentationObjectFactory::RegisterIOFactories() { } struct RegisterSegmentationObjectFactory { RegisterSegmentationObjectFactory() : m_Factory(mitk::SegmentationObjectFactory::New()) { mitk::CoreObjectFactory::GetInstance()->RegisterExtraFactory(m_Factory); } ~RegisterSegmentationObjectFactory() { mitk::CoreObjectFactory::GetInstance()->UnRegisterExtraFactory(m_Factory); } mitk::SegmentationObjectFactory::Pointer m_Factory; }; static RegisterSegmentationObjectFactory registerSegmentationObjectFactory; diff --git a/Modules/Segmentation/CMakeLists.txt b/Modules/Segmentation/CMakeLists.txt index 865c34266e..1c31c05044 100644 --- a/Modules/Segmentation/CMakeLists.txt +++ b/Modules/Segmentation/CMakeLists.txt @@ -1,9 +1,9 @@ mitk_create_module( INCLUDE_DIRS Algorithms Controllers DataManagement Interactions Rendering SegmentationUtilities/BooleanOperations SegmentationUtilities/MorphologicalOperations DEPENDS MitkAlgorithmsExt MitkSurfaceInterpolation MitkGraphAlgorithms MitkContourModel MitkMultilabel PACKAGE_DEPENDS PUBLIC ITK|QuadEdgeMesh+RegionGrowing - PRIVATE ITK|LabelMap+Watersheds VTK|ImagingGeneral + PRIVATE ITK|LabelMap+MathematicalMorphology VTK|ImagingGeneral ) add_subdirectory(Testing) diff --git a/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp index 881f688fc3..903be794ef 100644 --- a/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp @@ -1,209 +1,209 @@ /*============================================================================ 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. ============================================================================*/ // MITK #include "mitkAutoMLSegmentationWithPreviewTool.h" #include "mitkImageAccessByItk.h" #include "mitkToolManager.h" #include #include #include #include #include #include #include // ITK #include #include mitk::AutoMLSegmentationWithPreviewTool::AutoMLSegmentationWithPreviewTool() : AutoSegmentationWithPreviewTool(true) { } void mitk::AutoMLSegmentationWithPreviewTool::SetSelectedLabels(const SelectedLabelVectorType& regions) { if (m_SelectedLabels != regions) { m_SelectedLabels = regions; //Note: we do not call this->Modified() on puprose. Reason: changing the //selected regions should not force to run otsu filter in DoUpdatePreview due to changed MTime. } } const mitk::LabelSetImage* mitk::AutoMLSegmentationWithPreviewTool::GetMLPreview() const { if (m_MLPreviewNode.IsNotNull()) { const auto mlPreviewImage = dynamic_cast(this->m_MLPreviewNode->GetData()); return mlPreviewImage; } return nullptr; } mitk::AutoMLSegmentationWithPreviewTool::SelectedLabelVectorType mitk::AutoMLSegmentationWithPreviewTool::GetSelectedLabels() const { return this->m_SelectedLabels; } void mitk::AutoMLSegmentationWithPreviewTool::Activated() { Superclass::Activated(); m_SelectedLabels = {}; m_MLPreviewNode = mitk::DataNode::New(); m_MLPreviewNode->SetProperty("name", StringProperty::New(std::string(this->GetName()) + "ML preview")); m_MLPreviewNode->SetProperty("helper object", BoolProperty::New(true)); m_MLPreviewNode->SetVisibility(true); m_MLPreviewNode->SetOpacity(1.0); this->GetToolManager()->GetDataStorage()->Add(m_MLPreviewNode); } void mitk::AutoMLSegmentationWithPreviewTool::Deactivated() { this->GetToolManager()->GetDataStorage()->Remove(m_MLPreviewNode); m_MLPreviewNode = nullptr; Superclass::Deactivated(); } void mitk::AutoMLSegmentationWithPreviewTool::UpdateCleanUp() { if (m_MLPreviewNode.IsNotNull()) m_MLPreviewNode->SetVisibility(m_SelectedLabels.empty()); if (nullptr != this->GetPreviewSegmentationNode()) this->GetPreviewSegmentationNode()->SetVisibility(!m_SelectedLabels.empty()); if (m_SelectedLabels.empty()) { - this->ResetPreviewNode(); + this->ResetPreviewContent(); } } void mitk::AutoMLSegmentationWithPreviewTool::SetNodeProperties(LabelSetImage::Pointer newMLPreview) { if (newMLPreview.IsNotNull()) { this->m_MLPreviewNode->SetData(newMLPreview); this->m_MLPreviewNode->SetProperty("binary", mitk::BoolProperty::New(false)); mitk::RenderingModeProperty::Pointer renderingMode = mitk::RenderingModeProperty::New(); renderingMode->SetValue(mitk::RenderingModeProperty::LOOKUPTABLE_LEVELWINDOW_COLOR); this->m_MLPreviewNode->SetProperty("Image Rendering.Mode", renderingMode); mitk::LookupTable::Pointer lut = mitk::LookupTable::New(); mitk::LookupTableProperty::Pointer prop = mitk::LookupTableProperty::New(lut); vtkSmartPointer lookupTable = vtkSmartPointer::New(); lookupTable->SetHueRange(1.0, 0.0); lookupTable->SetSaturationRange(1.0, 1.0); lookupTable->SetValueRange(1.0, 1.0); lookupTable->SetTableRange(-1.0, 1.0); lookupTable->Build(); lut->SetVtkLookupTable(lookupTable); prop->SetLookupTable(lut); this->m_MLPreviewNode->SetProperty("LookupTable", prop); mitk::LevelWindowProperty::Pointer levWinProp = mitk::LevelWindowProperty::New(); mitk::LevelWindow levelwindow; levelwindow.SetRangeMinMax(0, newMLPreview->GetStatistics()->GetScalarValueMax()); levWinProp->SetLevelWindow(levelwindow); this->m_MLPreviewNode->SetProperty("levelwindow", levWinProp); } } void mitk::AutoMLSegmentationWithPreviewTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, Image* previewImage, TimeStepType timeStep) { const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (nullptr == m_MLPreviewNode->GetData() || this->GetMTime() > m_MLPreviewNode->GetData()->GetMTime() || this->m_LastMLTimeStep != timeStep //this covers the case where dynamic //segmentations have to compute a preview //for all time steps on confirmation || this->GetLastTimePointOfUpdate() != timePoint //this ensures that static seg //previews work with dynamic images //with avoiding unnecessary other computations ) { if (nullptr == inputAtTimeStep) { MITK_WARN << "Cannot run segementation. Currently selected input image is not set."; return; } this->m_LastMLTimeStep = timeStep; auto newMLPreview = ComputeMLPreview(inputAtTimeStep, timeStep); this->SetNodeProperties(newMLPreview); } if (!m_SelectedLabels.empty()) { const auto mlPreviewImage = this->GetMLPreview(); if (nullptr != mlPreviewImage) { AccessByItk_n(mlPreviewImage, CalculateMergedSimplePreview, (previewImage, timeStep)); } } } template void mitk::AutoMLSegmentationWithPreviewTool::CalculateMergedSimplePreview(const itk::Image* itkImage, mitk::Image* segmentation, unsigned int timeStep) { typedef itk::Image InputImageType; typedef itk::Image OutputImageType; typedef itk::BinaryThresholdImageFilter FilterType; typename FilterType::Pointer filter = FilterType::New(); // InputImageType::Pointer itkImage; typename OutputImageType::Pointer itkBinaryResultImage; filter->SetInput(itkImage); filter->SetLowerThreshold(m_SelectedLabels[0]); filter->SetUpperThreshold(m_SelectedLabels[0]); - filter->SetInsideValue(1); + filter->SetInsideValue(this->GetUserDefinedActiveLabel()); filter->SetOutsideValue(0); filter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); filter->Update(); itkBinaryResultImage = filter->GetOutput(); itkBinaryResultImage->DisconnectPipeline(); // if more than one region id is used compute the union of all given binary regions for (const auto labelID : m_SelectedLabels) { if (labelID != m_SelectedLabels[0]) { filter->SetLowerThreshold(labelID); filter->SetUpperThreshold(labelID); - filter->SetInsideValue(1); + filter->SetInsideValue(this->GetUserDefinedActiveLabel()); filter->SetOutsideValue(0); filter->Update(); typename OutputImageType::Pointer tempImage = filter->GetOutput(); typename itk::OrImageFilter::Pointer orFilter = itk::OrImageFilter::New(); orFilter->SetInput1(tempImage); orFilter->SetInput2(itkBinaryResultImage); orFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); orFilter->UpdateLargestPossibleRegion(); itkBinaryResultImage = orFilter->GetOutput(); } } //---------------------------------------------------------------------------------------------------- segmentation->SetVolume((void*)(itkBinaryResultImage->GetPixelContainer()->GetBufferPointer()), timeStep); } diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp index a206b7c65e..44e836b3d6 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.cpp @@ -1,130 +1,148 @@ /*============================================================================ 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 "mitkAutoSegmentationTool.h" #include "mitkImage.h" #include "mitkToolManager.h" #include mitk::AutoSegmentationTool::AutoSegmentationTool() : Tool("dummy"), m_OverwriteExistingSegmentation(false) { } mitk::AutoSegmentationTool::AutoSegmentationTool(const char* interactorType, const us::Module* interactorModule) : Tool(interactorType, interactorModule), m_OverwriteExistingSegmentation(false) { } mitk::AutoSegmentationTool::~AutoSegmentationTool() { } void mitk::AutoSegmentationTool::Activated() { Superclass::Activated(); m_NoneOverwriteTargetSegmentationNode = nullptr; } void mitk::AutoSegmentationTool::Deactivated() { m_NoneOverwriteTargetSegmentationNode = nullptr; Superclass::Deactivated(); } const char *mitk::AutoSegmentationTool::GetGroup() const { return "autoSegmentation"; } mitk::Image::ConstPointer mitk::AutoSegmentationTool::GetImageByTimeStep(const mitk::Image* image, unsigned int timestep) { if (nullptr == image) return image; if (image->GetDimension() != 4) return image; mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(static_cast(timestep)); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } +mitk::Image::Pointer mitk::AutoSegmentationTool::GetImageByTimeStep(mitk::Image* image, unsigned int timestep) +{ + if (nullptr == image) + return image; + + if (image->GetDimension() != 4) + return image; + + mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); + + imageTimeSelector->SetInput(image); + imageTimeSelector->SetTimeNr(static_cast(timestep)); + + imageTimeSelector->UpdateLargestPossibleRegion(); + + return imageTimeSelector->GetOutput(); +} + mitk::Image::ConstPointer mitk::AutoSegmentationTool::GetImageByTimePoint(const mitk::Image* image, TimePointType timePoint) { if (nullptr == image) return image; if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) return nullptr; return AutoSegmentationTool::GetImageByTimeStep(image, image->GetTimeGeometry()->TimePointToTimeStep(timePoint)); } void mitk::AutoSegmentationTool::SetOverwriteExistingSegmentation(bool overwrite) { if (m_OverwriteExistingSegmentation != overwrite) { m_OverwriteExistingSegmentation = overwrite; m_NoneOverwriteTargetSegmentationNode = nullptr; } } std::string mitk::AutoSegmentationTool::GetCurrentSegmentationName() { if (this->GetToolManager()->GetWorkingData(0)) return this->GetToolManager()->GetWorkingData(0)->GetName(); else return ""; } mitk::DataNode *mitk::AutoSegmentationTool::GetTargetSegmentationNode() const { mitk::DataNode::Pointer segmentationNode = this->GetToolManager()->GetWorkingData(0); if (!m_OverwriteExistingSegmentation) { if (m_NoneOverwriteTargetSegmentationNode.IsNull()) { mitk::DataNode::Pointer refNode = this->GetToolManager()->GetReferenceData(0); if (refNode.IsNull()) { // TODO create and use segmentation exceptions instead!! MITK_ERROR << "No valid reference data!"; return nullptr; } std::string nodename = refNode->GetName() + "_" + this->GetName(); mitk::Color color; color.SetRed(1); color.SetBlue(0); color.SetGreen(0); //create a new segmentation node based on the current segmentation as template m_NoneOverwriteTargetSegmentationNode = CreateEmptySegmentationNode(dynamic_cast(segmentationNode->GetData()), nodename, color); } segmentationNode = m_NoneOverwriteTargetSegmentationNode; } return segmentationNode; } void mitk::AutoSegmentationTool::EnsureTargetSegmentationNodeInDataStorage() const { auto targetNode = this->GetTargetSegmentationNode(); if (!this->GetToolManager()->GetDataStorage()->Exists(targetNode)) { this->GetToolManager()->GetDataStorage()->Add(targetNode, this->GetToolManager()->GetReferenceData(0)); } } diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h index 69624986c8..8665208340 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationTool.h @@ -1,84 +1,86 @@ /*============================================================================ 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 mitkAutoSegmentationTool_h_Included #define mitkAutoSegmentationTool_h_Included #include "mitkCommon.h" #include "mitkTool.h" #include namespace mitk { class Image; /** \brief Superclass for tool that create a new segmentation without user interaction in render windows This class is undocumented. Ask the creator ($Author$) to supply useful comments. */ class MITKSEGMENTATION_EXPORT AutoSegmentationTool : public Tool { public: mitkClassMacro(AutoSegmentationTool, Tool); void Activated() override; void Deactivated() override; /** This function controls wether a confirmed segmentation should replace the old * segmentation/working node (true) or if it should be stored as new and additional * node (false). */ void SetOverwriteExistingSegmentation(bool overwrite); /** * @brief Gets the name of the currently selected segmentation node * @return the name of the segmentation node or an empty string if * none is selected */ std::string GetCurrentSegmentationName(); /** * @brief Depending on the selected mode either returns the currently selected segmentation node * or (if overwrite mode is false) creates a new one from the selected reference data. * @remark Please keep in mind that new created nodes are not automatically added to the data storage. * Derived tools can call EnsureTargetSegmentationNodeInDataStorage to ensure it as soon as it is clear * that the target segmentation node will be/is confirmed. * @return a mitk::DataNode which contains a segmentation image */ virtual DataNode *GetTargetSegmentationNode() const; protected: AutoSegmentationTool(); // purposely hidden AutoSegmentationTool(const char* interactorType, const us::Module* interactorModule = nullptr); // purposely hidden ~AutoSegmentationTool() override; const char *GetGroup() const override; /** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/ static Image::ConstPointer GetImageByTimeStep(const Image* image, unsigned int timestep); + /** Helper that extracts the image for the passed timestep, if the image has multiple time steps.*/ + static Image::Pointer GetImageByTimeStep(Image* image, unsigned int timestep); /** Helper that extracts the image for the passed time point, if the image has multiple time steps.*/ static Image::ConstPointer GetImageByTimePoint(const Image* image, TimePointType timePoint); void EnsureTargetSegmentationNodeInDataStorage() const; bool m_OverwriteExistingSegmentation; private: /**Contains the node returned by GetTargetSementationNode if m_OverwriteExistingSegmentation == false. Then * GetTargetSegmentation generates a new target segmentation node.*/ mutable DataNode::Pointer m_NoneOverwriteTargetSegmentationNode; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp index ac0dacbd91..2ddbe65aec 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.cpp @@ -1,559 +1,736 @@ /*============================================================================ 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 "mitkAutoSegmentationWithPreviewTool.h" #include "mitkToolManager.h" #include "mitkColorProperty.h" #include "mitkLevelWindowProperty.h" #include "mitkProperties.h" #include "mitkDataStorage.h" #include "mitkRenderingManager.h" #include #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageStatisticsHolder.h" #include "mitkImageTimeSelector.h" #include "mitkLabelSetImage.h" #include "mitkMaskAndCutRoiImageFilter.h" #include "mitkPadImageFilter.h" #include "mitkNodePredicateGeometry.h" #include "mitkSegTool2D.h" +#include + mitk::AutoSegmentationWithPreviewTool::AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews): m_LazyDynamicPreviews(lazyDynamicPreviews) { m_ProgressCommand = ToolCommand::New(); } mitk::AutoSegmentationWithPreviewTool::AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule) : AutoSegmentationTool(interactorType, interactorModule), m_LazyDynamicPreviews(lazyDynamicPreviews) { m_ProgressCommand = ToolCommand::New(); } mitk::AutoSegmentationWithPreviewTool::~AutoSegmentationWithPreviewTool() { } bool mitk::AutoSegmentationWithPreviewTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const { if (!Superclass::CanHandle(referenceData, workingData)) return false; if (workingData == nullptr) return true; auto* labelSet = dynamic_cast(workingData); if (labelSet != nullptr) return true; auto* image = dynamic_cast(workingData); if (image == nullptr) return false; //if it is a normal image and not a label set image is used as working data //it must have the same pixel type as a label set. return MakeScalarPixelType< DefaultSegmentationDataType >() == image->GetPixelType(); } void mitk::AutoSegmentationWithPreviewTool::Activated() { Superclass::Activated(); this->GetToolManager()->RoiDataChanged += MessageDelegate(this, &AutoSegmentationWithPreviewTool::OnRoiDataChanged); this->GetToolManager()->SelectedTimePointChanged += MessageDelegate(this, &AutoSegmentationWithPreviewTool::OnTimePointChanged); m_ReferenceDataNode = this->GetToolManager()->GetReferenceData(0); m_SegmentationInputNode = m_ReferenceDataNode; m_LastTimePointOfUpdate = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (m_PreviewSegmentationNode.IsNull()) { m_PreviewSegmentationNode = DataNode::New(); m_PreviewSegmentationNode->SetProperty("color", ColorProperty::New(0.0, 1.0, 0.0)); m_PreviewSegmentationNode->SetProperty("name", StringProperty::New(std::string(this->GetName())+" preview")); m_PreviewSegmentationNode->SetProperty("opacity", FloatProperty::New(0.3)); m_PreviewSegmentationNode->SetProperty("binary", BoolProperty::New(true)); m_PreviewSegmentationNode->SetProperty("helper object", BoolProperty::New(true)); } if (m_SegmentationInputNode.IsNotNull()) { this->ResetPreviewNode(); this->InitiateToolByInput(); } else { this->GetToolManager()->ActivateTool(-1); } } void mitk::AutoSegmentationWithPreviewTool::Deactivated() { this->GetToolManager()->RoiDataChanged -= MessageDelegate(this, &AutoSegmentationWithPreviewTool::OnRoiDataChanged); this->GetToolManager()->SelectedTimePointChanged -= MessageDelegate(this, &AutoSegmentationWithPreviewTool::OnTimePointChanged); m_SegmentationInputNode = nullptr; m_ReferenceDataNode = nullptr; m_WorkingPlaneGeometry = nullptr; try { if (DataStorage *storage = this->GetToolManager()->GetDataStorage()) { storage->Remove(m_PreviewSegmentationNode); RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (...) { // don't care } if (m_PreviewSegmentationNode.IsNotNull()) { m_PreviewSegmentationNode->SetData(nullptr); } Superclass::Deactivated(); } void mitk::AutoSegmentationWithPreviewTool::ConfirmSegmentation() { - if (m_LazyDynamicPreviews && m_CreateAllTimeSteps) + bool labelChanged = this->EnsureUpToDateUserDefinedActiveLabel(); + if ((m_LazyDynamicPreviews && m_CreateAllTimeSteps) || labelChanged) { // The tool should create all time steps but is currently in lazy mode, // thus ensure that a preview for all time steps is available. this->UpdatePreview(true); } + CreateResultSegmentationFromPreview(); RenderingManager::GetInstance()->RequestUpdateAll(); if (!m_KeepActiveAfterAccept) { this->GetToolManager()->ActivateTool(-1); } } void mitk::AutoSegmentationWithPreviewTool::InitiateToolByInput() { //default implementation does nothing. //implement in derived classes to change behavior } mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetPreviewSegmentation() { if (m_PreviewSegmentationNode.IsNull()) { return nullptr; } return dynamic_cast(m_PreviewSegmentationNode->GetData()); } mitk::DataNode* mitk::AutoSegmentationWithPreviewTool::GetPreviewSegmentationNode() { return m_PreviewSegmentationNode; } const mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetSegmentationInput() const { if (m_SegmentationInputNode.IsNull()) { return nullptr; } return dynamic_cast(m_SegmentationInputNode->GetData()); } const mitk::Image* mitk::AutoSegmentationWithPreviewTool::GetReferenceData() const { if (m_ReferenceDataNode.IsNull()) { return nullptr; } return dynamic_cast(m_ReferenceDataNode->GetData()); } +template +void ClearBufferProcessing(ImageType* itkImage) +{ + itkImage->FillBuffer(0); +} + +void mitk::AutoSegmentationWithPreviewTool::ResetPreviewContentAtTimeStep(unsigned int timeStep) +{ + auto previewImage = GetImageByTimeStep(this->GetPreviewSegmentation(), timeStep); + if (nullptr != previewImage) + { + AccessByItk(previewImage, ClearBufferProcessing); + } +} + +void mitk::AutoSegmentationWithPreviewTool::ResetPreviewContent() +{ + auto previewImage = this->GetPreviewSegmentation(); + if (nullptr != previewImage) + { + auto castedPreviewImage = + dynamic_cast(previewImage); + if (nullptr == castedPreviewImage) mitkThrow() << "Application is on wrong state / invalid tool implementation. Preview image should always be of type LabelSetImage now."; + castedPreviewImage->ClearBuffer(); + } +} + void mitk::AutoSegmentationWithPreviewTool::ResetPreviewNode() { + if (m_IsUpdating) + { + mitkThrow() << "Used tool is implemented incorrectly. ResetPreviewNode is called while preview update is ongoing. Check implementation!"; + } + itk::RGBPixel previewColor; previewColor[0] = 0.0f; previewColor[1] = 1.0f; previewColor[2] = 0.0f; const auto image = this->GetSegmentationInput(); if (nullptr != image) { LabelSetImage::ConstPointer workingImage = dynamic_cast(this->GetToolManager()->GetWorkingData(0)->GetData()); if (workingImage.IsNotNull()) { auto newPreviewImage = workingImage->Clone(); if (this->GetResetsToEmptyPreview()) { newPreviewImage->ClearBuffer(); } if (newPreviewImage.IsNull()) { MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image"; return; } m_PreviewSegmentationNode->SetData(newPreviewImage); // Let's paint the feedback node green... auto* activeLayer = newPreviewImage->GetActiveLabelSet(); auto* activeLabel = activeLayer->GetActiveLabel(); activeLabel->SetColor(previewColor); activeLayer->UpdateLookupTable(activeLabel->GetValue()); } else { Image::ConstPointer workingImageBin = dynamic_cast(this->GetToolManager()->GetWorkingData(0)->GetData()); if (workingImageBin.IsNotNull()) { Image::Pointer newPreviewImage; if (this->GetResetsToEmptyPreview()) { newPreviewImage = Image::New(); newPreviewImage->Initialize(workingImageBin); } else { auto newPreviewImage = workingImageBin->Clone(); } if (newPreviewImage.IsNull()) { MITK_ERROR << "Cannot create preview helper objects. Unable to clone working image"; return; } m_PreviewSegmentationNode->SetData(newPreviewImage); } else { mitkThrow() << "Tool is an invalid state. Cannot setup preview node. Working data is an unsupported class and should have not been accepted by CanHandle()."; } } m_PreviewSegmentationNode->SetColor(previewColor); m_PreviewSegmentationNode->SetOpacity(0.5); int layer(50); m_ReferenceDataNode->GetIntProperty("layer", layer); m_PreviewSegmentationNode->SetIntProperty("layer", layer + 1); if (DataStorage *ds = this->GetToolManager()->GetDataStorage()) { if (!ds->Exists(m_PreviewSegmentationNode)) ds->Add(m_PreviewSegmentationNode, m_ReferenceDataNode); } } } -template -static void ITKSetVolume(const itk::Image *originalImage, - mitk::Image *segmentation, - unsigned int timeStep) -{ - auto constPixelContainer = originalImage->GetPixelContainer(); - //have to make a const cast because itk::PixelContainer does not provide a const correct access :( - auto pixelContainer = const_cast::PixelContainer*>(constPixelContainer); - - segmentation->SetVolume((void *)pixelContainer->GetBufferPointer(), timeStep); -} - void mitk::AutoSegmentationWithPreviewTool::TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep) { try { Image::ConstPointer sourceImageAtTimeStep = this->GetImageByTimeStep(sourceImage, timeStep); if (sourceImageAtTimeStep->GetPixelType() != destinationImage->GetPixelType()) { mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same pixel type. " << "Source pixel type: " << sourceImage->GetPixelType().GetTypeAsString() << "; destination pixel type: " << destinationImage->GetPixelType().GetTypeAsString(); } if (!Equal(*(sourceImage->GetGeometry(timeStep)), *(destinationImage->GetGeometry(timeStep)), NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_COORDINATE_PRECISION, NODE_PREDICATE_GEOMETRY_DEFAULT_CHECK_DIRECTION_PRECISION, false)) { mitkThrow() << "Cannot transfer images. Tool is in an invalid state, source image and destination image do not have the same geometry."; } if (nullptr != this->GetWorkingPlaneGeometry()) { auto sourceSlice = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), sourceImage, timeStep); SegTool2D::WriteBackSegmentationResult(this->GetTargetSegmentationNode(), m_WorkingPlaneGeometry, sourceSlice, timeStep); } else { //take care of the full segmentation volume - if (sourceImageAtTimeStep->GetDimension() == 2) - { - AccessFixedDimensionByItk_2( - sourceImageAtTimeStep, ITKSetVolume, 2, destinationImage, timeStep); - } - else - { - AccessFixedDimensionByItk_2( - sourceImageAtTimeStep, ITKSetVolume, 3, destinationImage, timeStep); - } + auto sourceLSImage = dynamic_cast(sourceImage); + auto destLSImage = dynamic_cast(destinationImage); + + TransferLabelContent(sourceLSImage, destLSImage, { {this->GetUserDefinedActiveLabel(),this->GetUserDefinedActiveLabel()} }, false, timeStep); } } catch (...) { Tool::ErrorMessage("Error accessing single time steps of the original image. Cannot create segmentation."); throw; } } void mitk::AutoSegmentationWithPreviewTool::CreateResultSegmentationFromPreview() { const auto segInput = this->GetSegmentationInput(); auto previewImage = this->GetPreviewSegmentation(); if (nullptr != segInput && nullptr != previewImage) { DataNode::Pointer resultSegmentationNode = GetTargetSegmentationNode(); if (resultSegmentationNode.IsNotNull()) { const auto timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); auto resultSegmentation = dynamic_cast(resultSegmentationNode->GetData()); // REMARK: the following code in this scope assumes that previewImage and resultSegmentation // are clones of the working image (segmentation provided to the tool). Therefore they have // the same time geometry. if (previewImage->GetTimeSteps() != resultSegmentation->GetTimeSteps()) { mitkThrow() << "Cannot perform threshold. Internal tool state is invalid." << " Preview segmentation and segmentation result image have different time geometries."; } if (m_CreateAllTimeSteps) { for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep) { this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep); } } else { const auto timeStep = resultSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint); this->TransferImageAtTimeStep(previewImage, resultSegmentation, timeStep); } // since we are maybe working on a smaller image, pad it to the size of the original image if (m_ReferenceDataNode.GetPointer() != m_SegmentationInputNode.GetPointer()) { PadImageFilter::Pointer padFilter = PadImageFilter::New(); padFilter->SetInput(0, resultSegmentation); padFilter->SetInput(1, dynamic_cast(m_ReferenceDataNode->GetData())); padFilter->SetBinaryFilter(true); padFilter->SetUpperThreshold(1); padFilter->SetLowerThreshold(1); padFilter->Update(); resultSegmentationNode->SetData(padFilter->GetOutput()); } if (m_OverwriteExistingSegmentation) { //if we overwrite the segmentation (and not just store it as a new result //in the data storage) we update also the tool manager state. this->GetToolManager()->SetWorkingData(resultSegmentationNode); this->GetToolManager()->GetWorkingData(0)->Modified(); } this->EnsureTargetSegmentationNodeInDataStorage(); } } } void mitk::AutoSegmentationWithPreviewTool::OnRoiDataChanged() { DataNode::ConstPointer node = this->GetToolManager()->GetRoiData(0); if (node.IsNotNull()) { MaskAndCutRoiImageFilter::Pointer roiFilter = MaskAndCutRoiImageFilter::New(); Image::Pointer image = dynamic_cast(m_SegmentationInputNode->GetData()); if (image.IsNull()) return; roiFilter->SetInput(image); roiFilter->SetRegionOfInterest(node->GetData()); roiFilter->Update(); DataNode::Pointer tmpNode = DataNode::New(); tmpNode->SetData(roiFilter->GetOutput()); m_SegmentationInputNode = tmpNode; } else m_SegmentationInputNode = m_ReferenceDataNode; this->ResetPreviewNode(); this->InitiateToolByInput(); this->UpdatePreview(); } void mitk::AutoSegmentationWithPreviewTool::OnTimePointChanged() { if (m_IsTimePointChangeAware && m_PreviewSegmentationNode.IsNotNull() && m_SegmentationInputNode.IsNotNull()) { const auto timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); const bool isStaticSegOnDynamicImage = m_PreviewSegmentationNode->GetData()->GetTimeSteps() == 1 && m_SegmentationInputNode->GetData()->GetTimeSteps() > 1; if (timePoint!=m_LastTimePointOfUpdate && (isStaticSegOnDynamicImage || m_LazyDynamicPreviews)) { //we only need to update either because we are lazzy //or because we have a static segmentation with a dynamic image this->UpdatePreview(); } } } -void mitk::AutoSegmentationWithPreviewTool::UpdatePreview(bool ignoreLazyPreviewSetting) +bool mitk::AutoSegmentationWithPreviewTool::EnsureUpToDateUserDefinedActiveLabel() { - const auto inputImage = this->GetSegmentationInput(); - auto previewImage = this->GetPreviewSegmentation(); - int progress_steps = 200; + bool labelChanged = true; const auto workingImage = dynamic_cast(this->GetToolManager()->GetWorkingData(0)->GetData()); if (const auto& labelSetImage = dynamic_cast(workingImage)) { // this is a fix for T28131 / T28986, which should be refactored if T28524 is being worked on - m_UserDefinedActiveLabel = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue(); + auto newLabel = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer())->GetValue(); + labelChanged = newLabel != m_UserDefinedActiveLabel; + m_UserDefinedActiveLabel = newLabel; } else { m_UserDefinedActiveLabel = 1; + labelChanged = false; } + return labelChanged; +} + +void mitk::AutoSegmentationWithPreviewTool::UpdatePreview(bool ignoreLazyPreviewSetting) +{ + const auto inputImage = this->GetSegmentationInput(); + auto previewImage = this->GetPreviewSegmentation(); + int progress_steps = 200; + + const auto workingImage = dynamic_cast(this->GetToolManager()->GetWorkingData(0)->GetData()); + this->EnsureUpToDateUserDefinedActiveLabel(); this->CurrentlyBusy.Send(true); m_IsUpdating = true; this->UpdatePrepare(); const auto timePoint = RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); try { if (nullptr != inputImage && nullptr != previewImage) { m_ProgressCommand->AddStepsToDo(progress_steps); if (previewImage->GetTimeSteps() > 1 && (ignoreLazyPreviewSetting || !m_LazyDynamicPreviews)) { for (unsigned int timeStep = 0; timeStep < previewImage->GetTimeSteps(); ++timeStep) { Image::ConstPointer feedBackImage; Image::ConstPointer currentSegImage; auto previewTimePoint = previewImage->GetTimeGeometry()->TimeStepToTimePoint(timeStep); auto inputTimeStep = inputImage->GetTimeGeometry()->TimePointToTimeStep(previewTimePoint); if (nullptr != this->GetWorkingPlaneGeometry()) { //only extract a specific slice defined by the working plane as feedback image. feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImage(this->GetWorkingPlaneGeometry(), inputImage, inputTimeStep); currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, previewTimePoint); } else { //work on the whole feedback image feedBackImage = this->GetImageByTimeStep(inputImage, inputTimeStep); currentSegImage = this->GetImageByTimePoint(workingImage, previewTimePoint); } this->DoUpdatePreview(feedBackImage, currentSegImage, previewImage, timeStep); } } else { Image::ConstPointer feedBackImage; Image::ConstPointer currentSegImage; if (nullptr != this->GetWorkingPlaneGeometry()) { feedBackImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), inputImage, timePoint); currentSegImage = SegTool2D::GetAffectedImageSliceAs2DImageByTimePoint(this->GetWorkingPlaneGeometry(), workingImage, timePoint); } else { feedBackImage = this->GetImageByTimePoint(inputImage, timePoint); currentSegImage = this->GetImageByTimePoint(workingImage, timePoint); } auto timeStep = previewImage->GetTimeGeometry()->TimePointToTimeStep(timePoint); this->DoUpdatePreview(feedBackImage, currentSegImage, previewImage, timeStep); } RenderingManager::GetInstance()->RequestUpdateAll(); } } catch (itk::ExceptionObject & excep) { MITK_ERROR << "Exception caught: " << excep.GetDescription(); m_ProgressCommand->SetProgress(progress_steps); std::string msg = excep.GetDescription(); ErrorMessage.Send(msg); } catch (...) { m_ProgressCommand->SetProgress(progress_steps); m_IsUpdating = false; CurrentlyBusy.Send(false); throw; } this->UpdateCleanUp(); m_LastTimePointOfUpdate = timePoint; m_ProgressCommand->SetProgress(progress_steps); m_IsUpdating = false; CurrentlyBusy.Send(false); } bool mitk::AutoSegmentationWithPreviewTool::IsUpdating() const { return m_IsUpdating; } void mitk::AutoSegmentationWithPreviewTool::UpdatePrepare() { // default implementation does nothing //reimplement in derived classes for special behavior } void mitk::AutoSegmentationWithPreviewTool::UpdateCleanUp() { // default implementation does nothing //reimplement in derived classes for special behavior } mitk::TimePointType mitk::AutoSegmentationWithPreviewTool::GetLastTimePointOfUpdate() const { return m_LastTimePointOfUpdate; } + +/** Functor class that implements the label transfer and is used in conjunction with the itk::BinaryFunctorImageFilter. +* For details regarding the usage of the filter and the functor patterns, please see info of itk::BinaryFunctorImageFilter. +*/ +template +class LabelTransferFunctor +{ + +public: + LabelTransferFunctor(){}; + + LabelTransferFunctor(const mitk::LabelSet* destinationLabelSet, mitk::Label::PixelType sourceBackground, + mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, + mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, bool mergeMode) : + m_DestinationLabelSet(destinationLabelSet), m_SourceBackground(sourceBackground), + m_DestinationBackground(destinationBackground), m_DestinationBackgroundLocked(destinationBackgroundLocked), + m_SourceLabel(sourceLabel), m_NewDestinationLabel(newDestinationLabel), m_MergeMode(mergeMode) + { + }; + + ~LabelTransferFunctor() {}; + + bool operator!=(const LabelTransferFunctor& other)const + { + return !(*this == other); + } + bool operator==(const LabelTransferFunctor& other) const + { + return this->m_SourceBackground == other.m_SourceBackground && + this->m_DestinationBackground == other.m_DestinationBackground && + this->m_DestinationBackgroundLocked == other.m_DestinationBackgroundLocked && + this->m_SourceLabel == other.m_SourceLabel && + this->m_NewDestinationLabel == other.m_NewDestinationLabel && + this->m_MergeMode == other.m_MergeMode && + this->m_DestinationLabelSet == other.m_DestinationLabelSet; + } + + LabelTransferFunctor& operator=(const LabelTransferFunctor& other) + { + this->m_DestinationLabelSet = other.m_DestinationLabelSet; + this->m_SourceBackground = other.m_SourceBackground; + this->m_DestinationBackground = other.m_DestinationBackground; + this->m_DestinationBackgroundLocked = other.m_DestinationBackgroundLocked; + this->m_SourceLabel = other.m_SourceLabel; + this->m_NewDestinationLabel = other.m_NewDestinationLabel; + this->m_MergeMode = other.m_MergeMode; + + return *this; + } + + inline TOutputpixel operator()(const TDestinationPixel& existingDestinationValue, const TSourcePixel& existingSourceValue) + { + if (existingSourceValue == this->m_SourceLabel + && !this->m_DestinationLabelSet->GetLabel(existingDestinationValue)->GetLocked()) + { + return this->m_NewDestinationLabel; + } + else if (!this->m_MergeMode + && existingSourceValue == this->m_SourceBackground + && existingDestinationValue == this->m_NewDestinationLabel + && !this->m_DestinationBackgroundLocked) + { + return this->m_DestinationBackground; + } + + return existingDestinationValue; + } + +private: + const mitk::LabelSet* m_DestinationLabelSet = nullptr; + mitk::Label::PixelType m_SourceBackground = 0; + mitk::Label::PixelType m_DestinationBackground = 0; + bool m_DestinationBackgroundLocked = false; + mitk::Label::PixelType m_SourceLabel = 1; + mitk::Label::PixelType m_NewDestinationLabel = 1; + bool m_MergeMode = false; +}; + +/**Helper function used by TransferLabelContent to allow the templating over different image dimensions in conjunction of AccessFixedPixelTypeByItk_n.*/ +template +void TransferLabelContentHelper(const itk::Image* itkSourceImage, mitk::Image* destinationImage, const mitk::LabelSet* destinationLabelSet, mitk::Label::PixelType sourceBackground, mitk::Label::PixelType destinationBackground, bool destinationBackgroundLocked, mitk::Label::PixelType sourceLabel, mitk::Label::PixelType newDestinationLabel, bool mergeMode) +{ + typedef itk::Image ContentImageType; + typename ContentImageType::Pointer itkDestinationImage; + mitk::CastToItkImage(destinationImage, itkDestinationImage); + + typedef LabelTransferFunctor LabelTransferFunctorType; + typedef itk::BinaryFunctorImageFilter FilterType; + + LabelTransferFunctorType transferFunctor(destinationLabelSet, sourceBackground, destinationBackground, + destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeMode); + + auto transferFilter = FilterType::New(); + + transferFilter->SetFunctor(transferFunctor); + transferFilter->InPlaceOn(); + transferFilter->SetInput1(itkDestinationImage); + transferFilter->SetInput2(itkSourceImage); + + transferFilter->Update(); +} + +void mitk::AutoSegmentationWithPreviewTool::TransferLabelContent( + const LabelSetImage* sourceImage, LabelSetImage* destinationImage, std::vector > labelMapping, bool mergeMode, const TimeStepType timeStep) +{ + if (nullptr == sourceImage) + { + mitkThrow() << "Invalid call of TransferLabelContent; sourceImage must not be null."; + } + if (nullptr == destinationImage) + { + mitkThrow() << "Invalid call of TransferLabelContent; destinationImage must not be null."; + } + + const auto sourceBackground = sourceImage->GetExteriorLabel()->GetValue(); + const auto destinationBackground = destinationImage->GetExteriorLabel()->GetValue(); + const auto destinationBackgroundLocked = destinationImage->GetExteriorLabel()->GetLocked(); + const auto destinationLabelSet = destinationImage->GetLabelSet(destinationImage->GetActiveLayer()); + + Image::ConstPointer sourceImageAtTimeStep = this->GetImageByTimeStep(sourceImage, timeStep); + Image::Pointer destinationImageAtTimeStep = this->GetImageByTimeStep(destinationImage, timeStep); + + if (nullptr == sourceImageAtTimeStep) + { + mitkThrow() << "Invalid call of TransferLabelContent; sourceImage does not have the requested time step: "<ExistLabel(sourceLabel, sourceImage->GetActiveLayer())) + { + mitkThrow() << "Invalid call of TransferLabelContent. Defined source label does not exist in sourceImage. SourceLabel: "<ExistLabel(newDestinationLabel, destinationImage->GetActiveLayer())) + { + mitkThrow() << "Invalid call of TransferLabelContent. Defined destination label does not exist in destinationImage. newDestinationLabel: " << newDestinationLabel; + } + + + AccessFixedPixelTypeByItk_n(sourceImageAtTimeStep, TransferLabelContentHelper, (Label::PixelType), (destinationImageAtTimeStep, destinationLabelSet, sourceBackground, destinationBackground, destinationBackgroundLocked, sourceLabel, newDestinationLabel, mergeMode)); + } + destinationImage->Modified(); +} diff --git a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h index b42f2c4396..a7af6f020b 100644 --- a/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h +++ b/Modules/Segmentation/Interactions/mitkAutoSegmentationWithPreviewTool.h @@ -1,190 +1,231 @@ /*============================================================================ 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 mitkAutoSegmentationWithPreviewTool_h_Included #define mitkAutoSegmentationWithPreviewTool_h_Included #include "mitkAutoSegmentationTool.h" #include "mitkCommon.h" #include "mitkDataNode.h" #include "mitkToolCommand.h" #include namespace mitk { /** \brief Base class for any auto segmentation tool that provides a preview of the new segmentation. This tool class implements a lot basic logic to handle auto segmentation tools with preview, Time point and ROI support. Derived classes will ask to update the segmentation preview if needed (e.g. because the ROI or the current time point has changed) or because derived tools indicated the need to update themselves. This class also takes care to properly transfer a confirmed preview into the segementation result. \ingroup ToolManagerEtAl \sa mitk::Tool \sa QmitkInteractiveSegmentation */ class MITKSEGMENTATION_EXPORT AutoSegmentationWithPreviewTool : public AutoSegmentationTool { public: mitkClassMacro(AutoSegmentationWithPreviewTool, AutoSegmentationTool); void Activated() override; void Deactivated() override; void ConfirmSegmentation(); itkSetMacro(CreateAllTimeSteps, bool); itkGetMacro(CreateAllTimeSteps, bool); itkBooleanMacro(CreateAllTimeSteps); itkSetMacro(KeepActiveAfterAccept, bool); itkGetMacro(KeepActiveAfterAccept, bool); itkBooleanMacro(KeepActiveAfterAccept); itkSetMacro(IsTimePointChangeAware, bool); itkGetMacro(IsTimePointChangeAware, bool); itkBooleanMacro(IsTimePointChangeAware); itkSetMacro(ResetsToEmptyPreview, bool); itkGetMacro(ResetsToEmptyPreview, bool); itkBooleanMacro(ResetsToEmptyPreview); bool CanHandle(const BaseData* referenceData, const BaseData* workingData) const override; /** Triggers the actualization of the preview * @param ignoreLazyPreviewSetting If set true UpdatePreview will always * generate the preview for all time steps. If set to false, UpdatePreview * will regard the setting specified by the constructor. * To define the update generation for time steps implement DoUpdatePreview. * To alter what should be done directly before or after the update of the preview, * reimplement UpdatePrepare() or UpdateCleanUp().*/ void UpdatePreview(bool ignoreLazyPreviewSetting = false); /** Indicate if currently UpdatePreview is triggered (true) or not (false).*/ bool IsUpdating() const; protected: ToolCommand::Pointer m_ProgressCommand; /** Member is always called if GetSegmentationInput() has changed * (e.g. because a new ROI was defined, or on activation) to give derived * classes the posibility to initiate their state accordingly. * Reimplement this function to implement special behavior. */ virtual void InitiateToolByInput(); /** This member function offers derived classes the possibility to alter what should happen directly before the update of the preview is performed. It is called by UpdatePreview. Default implementation does nothing.*/ virtual void UpdatePrepare(); /** This member function offers derived classes the possibility to alter what should happen directly after the update of the preview is performed. It is called by UpdatePreview. Default implementation does nothing.*/ virtual void UpdateCleanUp(); /** This function does the real work. Here the preview for a given * input image should be computed and stored in the also passed * preview image at the passed time step. * It also provides the current/old segmentation at the time point, * which can be used, if the preview depends on the the segmenation so far. */ virtual void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, Image* previewImage, TimeStepType timeStep) = 0; AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews = false); // purposely hidden AutoSegmentationWithPreviewTool(bool lazyDynamicPreviews, const char* interactorType, const us::Module* interactorModule = nullptr); // purposely hidden ~AutoSegmentationWithPreviewTool() override; /** Returns the image that contains the preview of the current segmentation. * Returns null if the node is not set or does not contain an image.*/ Image* GetPreviewSegmentation(); DataNode* GetPreviewSegmentationNode(); /** Returns the input that should be used for any segmentation/preview or tool update. * It is either the data of ReferenceDataNode itself or a part of it defined by a ROI mask * provided by the tool manager. Derived classes should regard this as the relevant * input data for any processing. * Returns null if the node is not set or does not contain an image.*/ const Image* GetSegmentationInput() const; /** Returns the image that is provided by the ReferenceDataNode. * Returns null if the node is not set or does not contain an image.*/ const Image* GetReferenceData() const; - /** Resets the preview node so it is empty and ready to be filled by the tool*/ + /** Resets the preview node so it is empty and ready to be filled by the tool + @remark Calling this function will generate a new preview image, and the old + might be invalidated. Therefore this function should not be used within the + scope of UpdatePreview (m_IsUpdating == true).*/ void ResetPreviewNode(); + /** Resets the complete content of the preview image. The instance of the preview image and its settings + * stay the same.*/ + void ResetPreviewContent(); + + /** Resets only the image content of the specified timeStep of the preview image. If the preview image or the specified + time step does not exist, nothing happens.*/ + void ResetPreviewContentAtTimeStep(unsigned int timeStep); + TimePointType GetLastTimePointOfUpdate() const; itkGetConstMacro(UserDefinedActiveLabel, Label::PixelType); itkSetObjectMacro(WorkingPlaneGeometry, PlaneGeometry); itkGetConstObjectMacro(WorkingPlaneGeometry, PlaneGeometry); private: void TransferImageAtTimeStep(const Image* sourceImage, Image* destinationImage, const TimeStepType timeStep); + /**Helper function that transfers pixels of the specified source label from source image to the destination image by using + a specified destination label. Function processes the whole image volume of the specified time step. + @remark in its current implementation the function only transfers contents of the active layer of the passed LabelSetImages. + @remark the function assumes that it is only called with source and destination image of same geometry. + @param sourceImage Pointer to the LabelSetImage which active layer should be used as source for the transfer. + @param destionationImage Pointer to the LabelSetImage which active layer should be used as destination for the transfer. + @param labelMapping Map that encodes the mappings of all label pixel transfers that should be done. First element is the + label in the source image. The second element is the label that transferred pixels should become in the destination image. + @param mergeMode indicates how the transfer should be done. If true, it is performed like a merge/union operation. So only + pixels of the label will be added. If false, also background is transferred, if present in the source image where the + destinationImage is labeled by the destination label. Therefore in this mode the label in the destinationImage can + "shrink"/lose pixels to the background. + @param timeStep indicate the time step that should be transferred. + @pre sourceImage and destinationImage must be valid + @pre sourceImage and destinationImage must contain the indicated timeStep + @pre sourceImage must contain all indicated sourceLabels in its active layer. + @pre destinationImage must contain all indicated destinationLabels in its active layer.*/ + void TransferLabelContent(const LabelSetImage* sourceImage, LabelSetImage* destinationImage, + std::vector > labelMapping = { {1,1} }, + bool mergeMode = false, const TimeStepType timeStep = 0); + void CreateResultSegmentationFromPreview(); void OnRoiDataChanged(); void OnTimePointChanged(); + /**Internal helper that ensures that the stored active label is up to date. + This is a fix for T28131 / T28986. It should be refactored if T28524 is being worked on. + On the long run, the active label will be communicated/set by the user/toolmanager as a + state of the tool and the tool should react accordingly (like it does for other external + state changes). + @return indicates if the label has changed (true) or not. + */ + bool EnsureUpToDateUserDefinedActiveLabel(); + /** Node that containes the preview data generated and managed by this class or derived ones.*/ DataNode::Pointer m_PreviewSegmentationNode; /** The reference data recieved from ToolManager::GetReferenceData when tool was activated.*/ DataNode::Pointer m_ReferenceDataNode; /** Node that containes the data that should be used as input for any auto segmentation. It might * be the same like m_ReferenceDataNode (if no ROI is set) or a sub region (if ROI is set).*/ DataNode::Pointer m_SegmentationInputNode; /** Indicates if Accepting the threshold should transfer/create the segmentations of all time steps (true) or only of the currently selected timepoint (false).*/ bool m_CreateAllTimeSteps = false; /** Indicates if the tool should kept active after accepting the segmentation or not.*/ bool m_KeepActiveAfterAccept = false; /** Relevant if the working data / preview image has multiple time steps (dynamic segmentations). * This flag has to be set by derived classes accordingly to there way to generate dynamic previews. * If LazyDynamicPreview is true, the tool generates only the preview for the current time step. * Therefore it always has to update the preview if current time point has changed and it has to (re)compute * all timeframes if ConfirmSegmentation() is called.*/ bool m_LazyDynamicPreviews = false; bool m_IsTimePointChangeAware = true; /** Controls if ResetPreviewNode generates an empty content (true) or clones the current segmentation (false).*/ bool m_ResetsToEmptyPreview = false; TimePointType m_LastTimePointOfUpdate = 0.; bool m_IsUpdating = false; Label::PixelType m_UserDefinedActiveLabel = 1; /** This variable indicates if for the tool a working plane geometry is defined. * If a working plane is defined the tool will only work an the slice of the input * and the segmentation. Thus only the relevant input slice will be passed to * DoUpdatePreview(...) and only the relevant slice of the preview will be transfered when * ConfirmSegmentation() is called.*/ PlaneGeometry::Pointer m_WorkingPlaneGeometry; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkEraseRegionTool.h b/Modules/Segmentation/Interactions/mitkEraseRegionTool.h index c5008654b5..5316ce5dc1 100644 --- a/Modules/Segmentation/Interactions/mitkEraseRegionTool.h +++ b/Modules/Segmentation/Interactions/mitkEraseRegionTool.h @@ -1,61 +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 mitkEraseRegionTool_h_Included #define mitkEraseRegionTool_h_Included #include "mitkSetRegionTool.h" #include namespace us { class ModuleResource; } namespace mitk { /** - \brief Fill the inside of a contour with 1 + \brief Erase the inside of a contour by + filling the inside of a contour with the background pixel value. \sa SetRegionTool - \ingroup Interaction - \ingroup ToolManagerEtAl + \ingroup Interactions - Finds the outer contour of a shape in 2D (possibly including holes) and sets all - the inside pixels to 0 (erasing a segmentation). + Finds the outer contour of a shape in 2D (possibly including single patches) and sets all + the pixels inside to the background pixel value (erasing a segmentation). + If clicked on the background, the outer contour might contain the whole image and thus + fill the whole image with the background pixel value. \warning Only to be instantiated by mitk::ToolManager. - - $Author$ */ class MITKSEGMENTATION_EXPORT EraseRegionTool : public SetRegionTool { public: mitkClassMacro(EraseRegionTool, SetRegionTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char **GetXPM() const override; us::ModuleResource GetCursorIconResource() const override; us::ModuleResource GetIconResource() const override; const char *GetName() const override; protected: EraseRegionTool(); // purposely hidden ~EraseRegionTool() override; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.cpp deleted file mode 100644 index 86a4afd6bf..0000000000 --- a/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.cpp +++ /dev/null @@ -1,335 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkFastMarchingBaseTool.h" -#include "mitkToolManager.h" - -#include "mitkBaseRenderer.h" -#include "mitkInteractionConst.h" -#include "mitkRenderingManager.h" -#include "mitkInteractionPositionEvent.h" - -#include "mitkImageAccessByItk.h" - -#include "mitkSegTool2D.h" - -#include - -// itk filter -#include "itkBinaryThresholdImageFilter.h" -#include "itkCurvatureAnisotropicDiffusionImageFilter.h" -#include "itkGradientMagnitudeRecursiveGaussianImageFilter.h" -#include "itkSigmoidImageFilter.h" -#include "itkFastMarchingImageFilter.h" - -// us -#include -#include -#include -#include - - -mitk::FastMarchingBaseTool::FastMarchingBaseTool(unsigned int toolDim) - : AutoSegmentationWithPreviewTool(false, "FastMarchingTool"), - m_LowerThreshold(0), - m_UpperThreshold(200), - m_StoppingValue(100), - m_Sigma(1.0), - m_Alpha(-0.5), - m_Beta(3.0), - m_ToolDimension(toolDim) -{ -} - -mitk::FastMarchingBaseTool::~FastMarchingBaseTool() -{ -} - -bool mitk::FastMarchingBaseTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const -{ - if(!Superclass::CanHandle(referenceData, workingData)) - return false; - - if (referenceData == nullptr) - return false; - - auto *image = dynamic_cast(referenceData); - - if (image == nullptr) - return false; - - if (image->GetDimension() < 3) - return false; - - return true; -} - -const char **mitk::FastMarchingBaseTool::GetXPM() const -{ - return nullptr; // mitkFastMarchingBaseTool_xpm; -} - -us::ModuleResource mitk::FastMarchingBaseTool::GetIconResource() const -{ - us::Module *module = us::GetModuleContext()->GetModule(); - us::ModuleResource resource = module->GetResource("FastMarching_48x48.png"); - return resource; -} - -us::ModuleResource mitk::FastMarchingBaseTool::GetCursorIconResource() const -{ - us::Module* module = us::GetModuleContext()->GetModule(); - us::ModuleResource resource = module->GetResource("FastMarching_Cursor_32x32.png"); - return resource; -} - -void mitk::FastMarchingBaseTool::SetUpperThreshold(double value) -{ - m_UpperThreshold = value / 10.0; -} - -void mitk::FastMarchingBaseTool::SetLowerThreshold(double value) -{ - m_LowerThreshold = value / 10.0; -} - -void mitk::FastMarchingBaseTool::SetBeta(double value) -{ - if (m_Beta != value) - { - m_Beta = value; - } -} - -void mitk::FastMarchingBaseTool::SetSigma(double value) -{ - if (m_Sigma != value) - { - if (value > 0.0) - { - m_Sigma = value; - } - } -} - -void mitk::FastMarchingBaseTool::SetAlpha(double value) -{ - if (m_Alpha != value) - { - m_Alpha = value; - } -} - -void mitk::FastMarchingBaseTool::SetStoppingValue(double value) -{ - if (m_StoppingValue != value) - { - m_StoppingValue = value; - } -} - -void mitk::FastMarchingBaseTool::Activated() -{ - Superclass::Activated(); - - m_SeedsAsPointSet = PointSet::New(); - //ensure that the seed points are visible for all timepoints. - dynamic_cast(m_SeedsAsPointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); - - m_SeedsAsPointSetNode = DataNode::New(); - m_SeedsAsPointSetNode->SetData(m_SeedsAsPointSet); - m_SeedsAsPointSetNode->SetName(std::string(this->GetName()) + "_PointSet"); - m_SeedsAsPointSetNode->SetBoolProperty("helper object", true); - m_SeedsAsPointSetNode->SetColor(0.0, 1.0, 0.0); - m_SeedsAsPointSetNode->SetVisibility(true); - - this->GetDataStorage()->Add(m_SeedsAsPointSetNode, this->GetToolManager()->GetWorkingData(0)); -} - -void mitk::FastMarchingBaseTool::Deactivated() -{ - this->ClearSeeds(); - - this->GetDataStorage()->Remove(m_SeedsAsPointSetNode); - m_SeedsAsPointSetNode = nullptr; - m_SeedsAsPointSet = nullptr; - - Superclass::Deactivated(); -} - - -void mitk::FastMarchingBaseTool::ClearSeeds() -{ - if (this->m_SeedsAsPointSet.IsNotNull()) - { - // renew pointset - this->m_SeedsAsPointSet = PointSet::New(); - //ensure that the seed points are visible for all timepoints. - dynamic_cast(m_SeedsAsPointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); - this->m_SeedsAsPointSetNode->SetData(this->m_SeedsAsPointSet); - } -} - -void mitk::FastMarchingBaseTool::ConnectActionsAndFunctions() -{ - CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddPoint); - CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPoint); - CONNECT_FUNCTION("DeletePoint", OnDelete); -} - -void mitk::FastMarchingBaseTool::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) -{ - if (!this->IsUpdating() && m_SeedsAsPointSet.IsNotNull()) - { - const auto positionEvent = dynamic_cast(interactionEvent); - - if (positionEvent != nullptr) - { - auto workingPlaneGeometry = positionEvent->GetSender()->GetCurrentWorldPlaneGeometry(); - - // if click was on another plane and we are in 2D mode we should reset the seeds - if (m_ToolDimension == 2 && ( nullptr == this->GetWorkingPlaneGeometry() || !this->GetWorkingPlaneGeometry()->IsOnPlane(workingPlaneGeometry))) - { - this->ClearSeeds(); - this->SetWorkingPlaneGeometry(workingPlaneGeometry->Clone()); - } - - m_SeedsAsPointSet->InsertPoint(m_SeedsAsPointSet->GetSize(), positionEvent->GetPositionInWorld()); - this->UpdatePreview(); - } - } -} - -void mitk::FastMarchingBaseTool::OnDelete(StateMachineAction*, InteractionEvent* /*interactionEvent*/) -{ - if (!this->IsUpdating() && m_SeedsAsPointSet.IsNotNull()) - { - // delete last seed point - if (this->m_SeedsAsPointSet->GetSize() > 0) - { - m_SeedsAsPointSet->RemovePointAtEnd(0); - this->UpdatePreview(); - } - } -} - -void mitk::FastMarchingBaseTool::DoUpdatePreview(const Image* inputAtTimeStep, const Image* /*oldSegAtTimeStep*/, Image* previewImage, TimeStepType timeStep) -{ - if (nullptr != inputAtTimeStep && nullptr != previewImage && m_SeedsAsPointSet.IsNotNull() && m_SeedsAsPointSet->GetSize() > 0) - { - if (nullptr == this->GetWorkingPlaneGeometry()) - { - AccessFixedDimensionByItk_n(inputAtTimeStep, ITKFastMarching, 3, - (previewImage, timeStep, inputAtTimeStep->GetGeometry())); - } - else - { - AccessFixedDimensionByItk_n(inputAtTimeStep, ITKFastMarching, 2, - (previewImage, timeStep, inputAtTimeStep->GetGeometry())); - } - } -} - -template -void mitk::FastMarchingBaseTool::ITKFastMarching(const itk::Image* inputImage, - Image* segmentation, - unsigned int timeStep, - const BaseGeometry* inputGeometry) -{ - // typedefs for itk pipeline - typedef itk::Image InputImageType; - - typedef float InternalPixelType; - typedef itk::Image InternalImageType; - - typedef Tool::DefaultSegmentationDataType OutputPixelType; - typedef itk::Image OutputImageType; - - typedef itk::CurvatureAnisotropicDiffusionImageFilter SmoothingFilterType; - typedef itk::GradientMagnitudeRecursiveGaussianImageFilter GradientFilterType; - typedef itk::SigmoidImageFilter SigmoidFilterType; - typedef itk::BinaryThresholdImageFilter ThresholdingFilterType; - - typedef itk::FastMarchingImageFilter FastMarchingFilterType; - typedef typename FastMarchingFilterType::NodeContainer NodeContainer; - typedef typename FastMarchingFilterType::NodeType NodeType; - - //convert point set seed into trialpoint - typename NodeContainer::Pointer trialPoints = NodeContainer::New(); - trialPoints->Initialize(); - - for (auto pos = m_SeedsAsPointSet->Begin(); pos != m_SeedsAsPointSet->End(); ++pos) - { - Point3D clickInIndex; - - inputGeometry->WorldToIndex(pos->Value(), clickInIndex); - itk::Index seedPosition; - for (unsigned int dim = 0; dim < VImageDimension; ++dim) - { - seedPosition[dim] = clickInIndex[dim]; - } - - NodeType node; - const double seedValue = 0.0; - node.SetValue(seedValue); - node.SetIndex(seedPosition); - trialPoints->InsertElement(trialPoints->Size(), node); - } - - // assemble pipeline - auto smoothFilter = SmoothingFilterType::New(); - smoothFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - smoothFilter->SetTimeStep(0.05); - smoothFilter->SetNumberOfIterations(2); - smoothFilter->SetConductanceParameter(9.0); - - auto gradientMagnitudeFilter = GradientFilterType::New(); - gradientMagnitudeFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - gradientMagnitudeFilter->SetSigma(m_Sigma); - - auto sigmoidFilter = SigmoidFilterType::New(); - sigmoidFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - sigmoidFilter->SetAlpha(m_Alpha); - sigmoidFilter->SetBeta(m_Beta); - sigmoidFilter->SetOutputMinimum(0.0); - sigmoidFilter->SetOutputMaximum(1.0); - - auto fastMarchingFilter = FastMarchingFilterType::New(); - fastMarchingFilter->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - fastMarchingFilter->SetStoppingValue(m_StoppingValue); - fastMarchingFilter->SetTrialPoints(trialPoints); - - auto thresholdFilter = ThresholdingFilterType::New(); - thresholdFilter->SetLowerThreshold(m_LowerThreshold); - thresholdFilter->SetUpperThreshold(m_UpperThreshold); - thresholdFilter->SetInsideValue(this->GetUserDefinedActiveLabel()); - thresholdFilter->SetOutsideValue(0); - - // set up pipeline - smoothFilter->SetInput(inputImage); - gradientMagnitudeFilter->SetInput(smoothFilter->GetOutput()); - sigmoidFilter->SetInput(gradientMagnitudeFilter->GetOutput()); - fastMarchingFilter->SetInput(sigmoidFilter->GetOutput()); - thresholdFilter->SetInput(fastMarchingFilter->GetOutput()); - thresholdFilter->Update(); - - if (nullptr == this->GetWorkingPlaneGeometry()) - { - segmentation->SetVolume((void*)(thresholdFilter->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); - } - else - { - Image::Pointer sliceImage = Image::New(); - CastToMitkImage(thresholdFilter->GetOutput(), sliceImage); - SegTool2D::WriteSliceToVolume(segmentation, this->GetWorkingPlaneGeometry(), sliceImage, timeStep, false); - } -} diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.h b/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.h deleted file mode 100644 index 93d5bb0d61..0000000000 --- a/Modules/Segmentation/Interactions/mitkFastMarchingBaseTool.h +++ /dev/null @@ -1,117 +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. - -============================================================================*/ - -#ifndef mitkFastMarchingBaseTool_h_Included -#define mitkFastMarchingBaseTool_h_Included - -#include "mitkAutoSegmentationWithPreviewTool.h" -#include "mitkDataNode.h" -#include "mitkPointSet.h" -#include "mitkPointSetDataInteractor.h" -#include "mitkToolCommand.h" - -#include "itkImage.h" - -#include - -namespace us -{ - class ModuleResource; -} - -namespace mitk -{ - /** - \brief FastMarching semgentation tool base class. - - The segmentation is done by setting one or more seed points on the image - and adapting the time range and threshold. The pipeline is: - Smoothing->GradientMagnitude->SigmoidFunction->FastMarching->Threshold - The resulting binary image is seen as a segmentation of an object. - - For detailed documentation see ITK Software Guide section 9.3.1 Fast Marching Segmentation. - */ - class MITKSEGMENTATION_EXPORT FastMarchingBaseTool : public AutoSegmentationWithPreviewTool - { - public: - mitkClassMacro(FastMarchingBaseTool, AutoSegmentationWithPreviewTool); - - bool CanHandle(const BaseData* referenceData, const BaseData* workingData) const override; - - /* icon stuff */ - const char **GetXPM() const override; - us::ModuleResource GetCursorIconResource() const override; - us::ModuleResource GetIconResource() const override; - - void Activated() override; - void Deactivated() override; - - /// \brief Set parameter used in Threshold filter. - void SetUpperThreshold(double); - - /// \brief Set parameter used in Threshold filter. - void SetLowerThreshold(double); - - /// \brief Set parameter used in Fast Marching filter. - void SetStoppingValue(double); - - /// \brief Set parameter used in Gradient Magnitude filter. - void SetSigma(double); - - /// \brief Set parameter used in Fast Marching filter. - void SetAlpha(double); - - /// \brief Set parameter used in Fast Marching filter. - void SetBeta(double); - - /// \brief Clear all seed points. - void ClearSeeds(); - - - protected: - FastMarchingBaseTool(unsigned int toolDim); - ~FastMarchingBaseTool() override; - - void ConnectActionsAndFunctions() override; - - /// \brief Add point action of StateMachine pattern - virtual void OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent); - - /// \brief Delete action of StateMachine pattern - virtual void OnDelete(StateMachineAction*, InteractionEvent* interactionEvent); - - void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, Image* previewImage, TimeStepType timeStep) override; - - template - void ITKFastMarching(const itk::Image* inputImage, - Image* segmentation, unsigned int timeStep, const BaseGeometry* inputGeometry); - - private: - float m_LowerThreshold; // used in Threshold filter - float m_UpperThreshold; // used in Threshold filter - float m_StoppingValue; // used in Fast Marching filter - float m_Sigma; // used in GradientMagnitude filter - float m_Alpha; // used in Sigmoid filter - float m_Beta; // used in Sigmoid filter - - DataNode::Pointer m_SeedsAsPointSetNode; // used to visualize the seed points - PointSet::Pointer m_SeedsAsPointSet; - - /** Indicating if the tool is used in 2D mode (just segment the current slice) - * or 3D mode (segment the whole current volume),*/ - unsigned int m_ToolDimension; - - }; - -} // namespace - -#endif diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp deleted file mode 100644 index c4a58520e0..0000000000 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkFastMarchingTool.h" - - -namespace mitk -{ - MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, FastMarchingTool, "FastMarching tool"); -} - -mitk::FastMarchingTool::FastMarchingTool() - : FastMarchingBaseTool(2) -{ -} - -mitk::FastMarchingTool::~FastMarchingTool() -{ -} - -const char *mitk::FastMarchingTool::GetName() const -{ - return "Fast Marching"; -} - diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool.h b/Modules/Segmentation/Interactions/mitkFastMarchingTool.h deleted file mode 100644 index 9aa06c0b1a..0000000000 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool.h +++ /dev/null @@ -1,48 +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. - -============================================================================*/ - -#ifndef mitkFastMarchingTool_h_Included -#define mitkFastMarchingTool_h_Included - -#include "mitkFastMarchingBaseTool.h" - -#include - -namespace mitk -{ - /** - \brief FastMarching semgentation tool. - - The segmentation is done by setting one or more seed points on the image - and adapting the time range and threshold. The pipeline is: - Smoothing->GradientMagnitude->SigmoidFunction->FastMarching->Threshold - The resulting binary image is seen as a segmentation of an object. - - For detailed documentation see ITK Software Guide section 9.3.1 Fast Marching Segmentation. - */ - class MITKSEGMENTATION_EXPORT FastMarchingTool : public FastMarchingBaseTool - { - public: - mitkClassMacro(FastMarchingTool, FastMarchingBaseTool); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - const char *GetName() const override; - - protected: - FastMarchingTool(); - ~FastMarchingTool() override; - }; - -} // namespace - -#endif diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool.xpm b/Modules/Segmentation/Interactions/mitkFastMarchingTool.xpm deleted file mode 100644 index ee5b02c0bd..0000000000 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool.xpm +++ /dev/null @@ -1,53 +0,0 @@ -/* XPM */ -static const char * mitkFastMarchingTool_xpm[] = { -"48 48 2 1", -" c None", -". c #000000", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ...... ", -" .......... ", -" ........... ", -" ... ..... ", -" . ... ", -" ... ", -" ... ", -" ... ", -" ... ", -" .... ", -" .... ", -" ..... ", -" .... ", -" ... ", -" ... ", -" ... ", -" ", -" ", -" ", -" ... ", -" ... ", -" ... ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" ", -" "}; diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.cpp b/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.cpp deleted file mode 100644 index 0de2aacf30..0000000000 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.cpp +++ /dev/null @@ -1,32 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkFastMarchingTool3D.h" - -namespace mitk -{ - MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, FastMarchingTool3D, "FastMarching3D tool"); -} - -mitk::FastMarchingTool3D::FastMarchingTool3D() - : FastMarchingBaseTool(3) -{ -} - -mitk::FastMarchingTool3D::~FastMarchingTool3D() -{ -} - -const char *mitk::FastMarchingTool3D::GetName() const -{ - return "Fast Marching 3D"; -} diff --git a/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.h b/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.h deleted file mode 100644 index 88b82a6038..0000000000 --- a/Modules/Segmentation/Interactions/mitkFastMarchingTool3D.h +++ /dev/null @@ -1,49 +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. - -============================================================================*/ - -#ifndef mitkFastMarchingTool3D_h_Included -#define mitkFastMarchingTool3D_h_Included - -#include "mitkFastMarchingBaseTool.h" - -#include - -namespace mitk -{ - /** - \brief FastMarching semgentation tool. - - The segmentation is done by setting one or more seed points on the image - and adapting the time range and threshold. The pipeline is: - Smoothing->GradientMagnitude->SigmoidFunction->FastMarching->Threshold - The resulting binary image is seen as a segmentation of an object. - - For detailed documentation see ITK Software Guide section 9.3.1 Fast Marching Segmentation. - */ - class MITKSEGMENTATION_EXPORT FastMarchingTool3D : public FastMarchingBaseTool - { - public: - mitkClassMacro(FastMarchingTool3D, FastMarchingBaseTool); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - /* icon stuff */ - const char *GetName() const override; - - protected: - FastMarchingTool3D(); - ~FastMarchingTool3D() override; - }; - -} // namespace - -#endif diff --git a/Modules/Segmentation/Interactions/mitkFillRegionTool.h b/Modules/Segmentation/Interactions/mitkFillRegionTool.h index c704623335..3643b13992 100644 --- a/Modules/Segmentation/Interactions/mitkFillRegionTool.h +++ b/Modules/Segmentation/Interactions/mitkFillRegionTool.h @@ -1,60 +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 mitkFillRegionTool_h_Included #define mitkFillRegionTool_h_Included #include "mitkSetRegionTool.h" #include namespace us { class ModuleResource; } namespace mitk { /** - \brief Fill the inside of a contour with 1 + \brief Fill the inside of a contour with the foreground pixel value. \sa SetRegionTool - \ingroup Interaction - \ingroup ToolManagerEtAl + \ingroup Interactions Finds the outer contour of a shape in 2D (possibly including holes) and sets all - the inside pixels to 1, filling holes in a segmentation. - \warning Only to be instantiated by mitk::ToolManager. + the pixels inside to the foreground pixel value (filling holes in a segmentation). + If clicked on the background, the outer contour might contain the whole image and thus + fill the whole image with the foreground pixel value. - $Author$ + \warning Only to be instantiated by mitk::ToolManager. */ class MITKSEGMENTATION_EXPORT FillRegionTool : public SetRegionTool { public: mitkClassMacro(FillRegionTool, SetRegionTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char **GetXPM() const override; us::ModuleResource GetCursorIconResource() const override; us::ModuleResource GetIconResource() const override; const char *GetName() const override; protected: FillRegionTool(); // purposely hidden ~FillRegionTool() override; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkPickingTool.cpp b/Modules/Segmentation/Interactions/mitkPickingTool.cpp index 52ab30dfad..b45438f6e3 100644 --- a/Modules/Segmentation/Interactions/mitkPickingTool.cpp +++ b/Modules/Segmentation/Interactions/mitkPickingTool.cpp @@ -1,215 +1,276 @@ /*============================================================================ 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 "mitkPickingTool.h" #include "mitkProperties.h" #include "mitkToolManager.h" #include "mitkInteractionPositionEvent.h" // us #include #include #include #include #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageTimeSelector.h" #include "mitkImageTimeSelector.h" #include #include - +#include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, PickingTool, "PickingTool"); } mitk::PickingTool::PickingTool() : AutoSegmentationWithPreviewTool(false, "PressMoveReleaseAndPointSetting") { this->ResetsToEmptyPreviewOn(); } mitk::PickingTool::~PickingTool() { } bool mitk::PickingTool::CanHandle(const BaseData* referenceData, const BaseData* workingData) const { if (!Superclass::CanHandle(referenceData,workingData)) return false; auto* image = dynamic_cast(referenceData); if (image == nullptr) return false; return true; } const char **mitk::PickingTool::GetXPM() const { return nullptr; } const char *mitk::PickingTool::GetName() const { return "Picking"; } us::ModuleResource mitk::PickingTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("Pick_48x48.png"); return resource; } void mitk::PickingTool::Activated() { Superclass::Activated(); m_PointSet = mitk::PointSet::New(); //ensure that the seed points are visible for all timepoints. dynamic_cast(m_PointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); m_PointSetNode = mitk::DataNode::New(); m_PointSetNode->SetData(m_PointSet); m_PointSetNode->SetName(std::string(this->GetName()) + "_PointSet"); m_PointSetNode->SetBoolProperty("helper object", true); m_PointSetNode->SetColor(0.0, 1.0, 0.0); m_PointSetNode->SetVisibility(true); this->GetDataStorage()->Add(m_PointSetNode, this->GetToolManager()->GetWorkingData(0)); } void mitk::PickingTool::Deactivated() { this->ClearSeeds(); // remove from data storage and disable interaction GetDataStorage()->Remove(m_PointSetNode); m_PointSetNode = nullptr; m_PointSet = nullptr; Superclass::Deactivated(); } void mitk::PickingTool::ConnectActionsAndFunctions() { CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPoint); CONNECT_FUNCTION("DeletePoint", OnDelete); } void mitk::PickingTool::OnAddPoint(StateMachineAction*, InteractionEvent* interactionEvent) { if (!this->IsUpdating() && m_PointSet.IsNotNull()) { const auto positionEvent = dynamic_cast(interactionEvent); if (positionEvent != nullptr) { m_PointSet->InsertPoint(m_PointSet->GetSize(), positionEvent->GetPositionInWorld()); this->UpdatePreview(); } } } void mitk::PickingTool::OnDelete(StateMachineAction*, InteractionEvent* /*interactionEvent*/) { if (!this->IsUpdating() && m_PointSet.IsNotNull()) { // delete last seed point if (this->m_PointSet->GetSize() > 0) { m_PointSet->RemovePointAtEnd(0); this->UpdatePreview(); } } } void mitk::PickingTool::ClearPicks() { this->ClearSeeds(); this->UpdatePreview(); } bool mitk::PickingTool::HasPicks() const { return this->m_PointSet.IsNotNull() && this->m_PointSet->GetSize()>0; } void mitk::PickingTool::ClearSeeds() { if (this->m_PointSet.IsNotNull()) { // renew pointset this->m_PointSet = mitk::PointSet::New(); //ensure that the seed points are visible for all timepoints. dynamic_cast(m_PointSet->GetTimeGeometry())->SetStepDuration(std::numeric_limits::max()); this->m_PointSetNode->SetData(this->m_PointSet); } } template void DoITKRegionGrowing(const itk::Image* oldSegImage, mitk::Image* segmentation, const mitk::PointSet* seedPoints, - unsigned int timeStep, const mitk::BaseGeometry* inputGeometry) + unsigned int timeStep, const mitk::BaseGeometry* inputGeometry, const mitk::Label::PixelType outputValue, + const mitk::Label::PixelType backgroundValue, + bool& emptyTimeStep) { typedef itk::Image InputImageType; + typedef itk::Image OutputImageType; typedef typename InputImageType::IndexType IndexType; - typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; - typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); + typedef itk::ConnectedThresholdImageFilter RegionGrowingFilterType; + + using IndexMapType = std::map < mitk::Label::PixelType, std::vector >; + + IndexMapType indexMap; // convert world coordinates to image indices for (auto pos = seedPoints->Begin(); pos != seedPoints->End(); ++pos) { IndexType seedIndex; inputGeometry->WorldToIndex(pos->Value(), seedIndex); - regionGrower->AddSeed(seedIndex); - } + const auto selectedLabel = oldSegImage->GetPixel(seedIndex); - // perform region growing in desired segmented region - regionGrower->SetInput(oldSegImage); + if (selectedLabel != backgroundValue) + { + indexMap[selectedLabel].push_back(seedIndex); + } + } - regionGrower->SetLower(static_cast(1)); - regionGrower->SetUpper(std::numeric_limits::max()); + typename OutputImageType::Pointer itkResultImage; try { - regionGrower->Update(); + bool first = true; + typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); + regionGrower->SetInput(oldSegImage); + regionGrower->SetReplaceValue(outputValue); + + for (const auto& [label, indeces] : indexMap) + { + // perform region growing in desired segmented region + regionGrower->ClearSeeds(); + for (const auto& index : indeces) + { + regionGrower->AddSeed(index); + } + + regionGrower->SetLower(label); + regionGrower->SetUpper(label); + + regionGrower->Update(); + + if (first) + { + itkResultImage = regionGrower->GetOutput(); + } + else + { + typename itk::OrImageFilter::Pointer orFilter = + itk::OrImageFilter::New(); + orFilter->SetInput1(regionGrower->GetOutput()); + orFilter->SetInput2(itkResultImage); + + orFilter->Update(); + itkResultImage = orFilter->GetOutput(); + } + first = false; + itkResultImage->DisconnectPipeline(); + } } - catch (const itk::ExceptionObject &) + catch (const itk::ExceptionObject&) { return; // can't work } catch (...) { return; } - segmentation->SetVolume((void*)(regionGrower->GetOutput()->GetPixelContainer()->GetBufferPointer()), timeStep); + if (itkResultImage.IsNotNull()) + { + segmentation->SetVolume((void*)(itkResultImage->GetPixelContainer()->GetBufferPointer()),timeStep); + } + emptyTimeStep = itkResultImage.IsNull(); + } void mitk::PickingTool::DoUpdatePreview(const Image* /*inputAtTimeStep*/, const Image* oldSegAtTimeStep, Image* previewImage, TimeStepType timeStep) { if (nullptr != oldSegAtTimeStep && nullptr != previewImage && m_PointSet.IsNotNull()) { - AccessFixedDimensionByItk_n(oldSegAtTimeStep, DoITKRegionGrowing, 3, (previewImage, this->m_PointSet, timeStep, oldSegAtTimeStep->GetGeometry())); + bool emptyTimeStep = true; + if (this->HasPicks()) + { + Label::PixelType backgroundValue = 0; + auto labelSetImage = dynamic_cast(oldSegAtTimeStep); + if (nullptr != labelSetImage) + { + backgroundValue = labelSetImage->GetExteriorLabel()->GetValue(); + } + AccessFixedDimensionByItk_n(oldSegAtTimeStep, DoITKRegionGrowing, 3, (previewImage, this->m_PointSet, timeStep, oldSegAtTimeStep->GetGeometry(), this->GetUserDefinedActiveLabel(), backgroundValue, emptyTimeStep)); + } + if (emptyTimeStep) + { + this->ResetPreviewContentAtTimeStep(timeStep); + } } } diff --git a/Modules/Segmentation/Interactions/mitkSetRegionTool.h b/Modules/Segmentation/Interactions/mitkSetRegionTool.h index fbaa1c2ecb..9fab9b8030 100644 --- a/Modules/Segmentation/Interactions/mitkSetRegionTool.h +++ b/Modules/Segmentation/Interactions/mitkSetRegionTool.h @@ -1,63 +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 mitkSetRegionTool_h_Included #define mitkSetRegionTool_h_Included #include "mitkCommon.h" #include "mitkFeedbackContourTool.h" #include namespace mitk { class Image; class StateMachineAction; class InteractionEvent; /** \brief Fills or erases a 2D region \sa FeedbackContourTool \sa ExtractImageFilter - \ingroup Interaction - \ingroup ToolManagerEtAl + \ingroup Interactions - Finds the outer contour of a shape in 2D (possibly including holes) and sets all + Finds the outer contour of a shape in 2D (possibly including holes or single patches) and sets all the inside pixels to a specified value. This might fill holes or erase segmentations. \warning Only to be instantiated by mitk::ToolManager. - - $Author$ */ class MITKSEGMENTATION_EXPORT SetRegionTool : public FeedbackContourTool { public: mitkClassMacro(SetRegionTool, FeedbackContourTool); protected: SetRegionTool(int paintingPixelValue = 1); // purposely hidden ~SetRegionTool() override; void ConnectActionsAndFunctions() override; void Activated() override; void Deactivated() override; virtual void OnMousePressed(StateMachineAction *, InteractionEvent *); virtual void OnMouseReleased(StateMachineAction *, InteractionEvent *); virtual void OnMouseMoved(StateMachineAction *, InteractionEvent *); int m_PaintingPixelValue; }; } // namespace #endif diff --git a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp b/Modules/Segmentation/Interactions/mitkWatershedTool.cpp deleted file mode 100644 index 358cade477..0000000000 --- a/Modules/Segmentation/Interactions/mitkWatershedTool.cpp +++ /dev/null @@ -1,163 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkWatershedTool.h" - -#include "mitkIOUtil.h" -#include "mitkITKImageImport.h" -#include "mitkImage.h" -#include "mitkLabelSetImage.h" -#include "mitkImageAccessByItk.h" -#include "mitkImageCast.h" -#include "mitkImageStatisticsHolder.h" -#include "mitkLevelWindowManager.h" -#include "mitkLookupTable.h" -#include "mitkLookupTableProperty.h" -#include "mitkProgressBar.h" -#include "mitkRenderingManager.h" -#include "mitkRenderingModeProperty.h" -#include "mitkToolCommand.h" -#include "mitkToolManager.h" -#include - -#include -#include -#include -#include - -#include - -#include -#include -#include - -namespace mitk -{ - MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, WatershedTool, "Watershed tool"); -} - - -void mitk::WatershedTool::Activated() -{ - Superclass::Activated(); - - m_Level = 0.0; - m_Threshold = 0.0; - - m_MagFilter = nullptr; - m_WatershedFilter = nullptr; - m_LastFilterInput = nullptr; -} - -us::ModuleResource mitk::WatershedTool::GetIconResource() const -{ - us::Module *module = us::GetModuleContext()->GetModule(); - us::ModuleResource resource = module->GetResource("Watershed_48x48.png"); - return resource; -} - -const char **mitk::WatershedTool::GetXPM() const -{ - return nullptr; -} - -const char *mitk::WatershedTool::GetName() const -{ - return "Watershed"; -} - -mitk::LabelSetImage::Pointer mitk::WatershedTool::ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType /*timeStep*/) -{ - mitk::LabelSetImage::Pointer labelSetOutput; - - try - { - mitk::Image::Pointer output; - bool inputChanged = inputAtTimeStep != m_LastFilterInput; - // create and run itk filter pipeline - AccessByItk_2(inputAtTimeStep, ITKWatershed, output, inputChanged); - - labelSetOutput = mitk::LabelSetImage::New(); - labelSetOutput->InitializeByLabeledImage(output); - } - catch (itk::ExceptionObject & e) - { - //force reset of filters as they might be in an invalid state now. - m_MagFilter = nullptr; - m_WatershedFilter = nullptr; - m_LastFilterInput = nullptr; - - MITK_ERROR << "Watershed Filter Error: " << e.GetDescription(); - } - - m_LastFilterInput = inputAtTimeStep; - - return labelSetOutput; -} - -template -void mitk::WatershedTool::ITKWatershed(const itk::Image* originalImage, - mitk::Image::Pointer& segmentation, bool inputChanged) -{ - typedef itk::WatershedImageFilter> WatershedFilter; - typedef itk::GradientMagnitudeRecursiveGaussianImageFilter, - itk::Image> - MagnitudeFilter; - - // We create the filter pipeline only once (if needed) and not everytime we - // generate the ml image preview. - // Reason: If only the levels are changed the update of the pipe line is very - // fast and we want to profit from this feature. - - // at first add a gradient magnitude filter - typename MagnitudeFilter::Pointer magnitude = dynamic_cast(m_MagFilter.GetPointer()); - if (magnitude.IsNull()) - { - magnitude = MagnitudeFilter::New(); - magnitude->SetSigma(1.0); - magnitude->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - m_MagFilter = magnitude.GetPointer(); - } - - if (inputChanged) - { - magnitude->SetInput(originalImage); - } - - // then add the watershed filter to the pipeline - typename WatershedFilter::Pointer watershed = dynamic_cast(m_WatershedFilter.GetPointer()); - if (watershed.IsNull()) - { - watershed = WatershedFilter::New(); - watershed->SetInput(magnitude->GetOutput()); - watershed->AddObserver(itk::ProgressEvent(), m_ProgressCommand); - m_WatershedFilter = watershed.GetPointer(); - } - - watershed->SetThreshold(m_Threshold); - watershed->SetLevel(m_Level); - watershed->Update(); - - // then make sure, that the output has the desired pixel type - typedef itk::CastImageFilter> - CastFilter; - typename CastFilter::Pointer cast = CastFilter::New(); - cast->SetInput(watershed->GetOutput()); - - // start the whole pipeline - cast->Update(); - - // since we obtain a new image from our pipeline, we have to make sure, that our mitk::Image::Pointer - // is responsible for the memory management of the output image - segmentation = mitk::GrabItkImageMemory(cast->GetOutput()); -} diff --git a/Modules/Segmentation/Interactions/mitkWatershedTool.h b/Modules/Segmentation/Interactions/mitkWatershedTool.h deleted file mode 100644 index a4bea005ec..0000000000 --- a/Modules/Segmentation/Interactions/mitkWatershedTool.h +++ /dev/null @@ -1,84 +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. - -============================================================================*/ - -#ifndef mitkWatershedTool_h_Included -#define mitkWatershedTool_h_Included - -#include "mitkAutoMLSegmentationWithPreviewTool.h" -#include "mitkCommon.h" -#include - -namespace us -{ - class ModuleResource; -} - -namespace mitk -{ - /** - \brief Simple watershed segmentation tool. - - \ingroup Interaction - \ingroup ToolManagerEtAl - - Wraps ITK Watershed Filter into tool concept of MITK. For more information look into ITK documentation. - - \warning Only to be instantiated by mitk::ToolManager. - */ - class MITKSEGMENTATION_EXPORT WatershedTool : public AutoMLSegmentationWithPreviewTool - { - public: - mitkClassMacro(WatershedTool, AutoMLSegmentationWithPreviewTool); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - - const char** GetXPM() const override; - const char* GetName() const override; - us::ModuleResource GetIconResource() const override; - - void Activated() override; - - itkSetMacro(Threshold, double); - itkGetConstMacro(Threshold, double); - - itkSetMacro(Level, double); - itkGetConstMacro(Level, double); - - protected: - WatershedTool() = default; - ~WatershedTool() = default; - - LabelSetImage::Pointer ComputeMLPreview(const Image* inputAtTimeStep, TimeStepType timeStep) override; - - /** \brief Threshold parameter of the ITK Watershed Image Filter. See ITK Documentation for more information. */ - double m_Threshold = 0.0; - /** \brief Threshold parameter of the ITK Watershed Image Filter. See ITK Documentation for more information. */ - double m_Level = 0.0; - -private: - /** \brief Creates and runs an ITK filter pipeline consisting of the filters: GradientMagnitude-, Watershed- and - * CastImageFilter. - * - * \param originalImage The input image, which is delivered by the AccessByItk macro. - * \param segmentation A pointer to the output image, which will point to the pipeline output after execution. - */ - template - void ITKWatershed(const itk::Image* originalImage, itk::SmartPointer& segmentation, bool inputChanged); - - itk::ProcessObject::Pointer m_MagFilter; - itk::ProcessObject::Pointer m_WatershedFilter; - mitk::Image::ConstPointer m_LastFilterInput; - }; - -} // namespace - -#endif diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.cpp b/Modules/Segmentation/Interactions/mitknnUnetTool.cpp index 65de25e8cf..99779403d0 100644 --- a/Modules/Segmentation/Interactions/mitknnUnetTool.cpp +++ b/Modules/Segmentation/Interactions/mitknnUnetTool.cpp @@ -1,334 +1,327 @@ /*============================================================================ 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 "mitknnUnetTool.h" #include "mitkIOUtil.h" #include "mitkProcessExecutor.h" #include #include #include #include #include namespace mitk { MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, nnUNetTool, "nnUNet tool"); } mitk::nnUNetTool::nnUNetTool() { this->SetMitkTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX")); } mitk::nnUNetTool::~nnUNetTool() { itksys::SystemTools::RemoveADirectory(this->GetMitkTempDir()); } void mitk::nnUNetTool::Activated() { Superclass::Activated(); } -void mitk::nnUNetTool::UpdateCleanUp() -{ - // This overriden method is intentionally left out for setting later upon demand - // in the `RenderOutputBuffer` method. -} - void mitk::nnUNetTool::RenderOutputBuffer() { if (m_OutputBuffer != nullptr) { Superclass::SetNodeProperties(m_OutputBuffer); try { if (nullptr != this->GetPreviewSegmentationNode()) { this->GetPreviewSegmentationNode()->SetVisibility(!this->GetSelectedLabels().empty()); } if (this->GetSelectedLabels().empty()) { this->ResetPreviewNode(); } } catch (const mitk::Exception &e) { MITK_INFO << e.GetDescription(); } } } -void mitk::nnUNetTool::SetNodeProperties(LabelSetImage::Pointer segmentation) +void mitk::nnUNetTool::SetOutputBuffer(LabelSetImage::Pointer segmentation) { - // This overriden method doesn't set node properties. Intentionally left out for setting later upon demand - // in the `RenderOutputBuffer` method. m_OutputBuffer = segmentation; } mitk::LabelSetImage::Pointer mitk::nnUNetTool::GetOutputBuffer() { return m_OutputBuffer; } void mitk::nnUNetTool::ClearOutputBuffer() { m_OutputBuffer = nullptr; } us::ModuleResource mitk::nnUNetTool::GetIconResource() const { us::Module *module = us::GetModuleContext()->GetModule(); us::ModuleResource resource = module->GetResource("AI_48x48.png"); return resource; } const char **mitk::nnUNetTool::GetXPM() const { return nullptr; } const char *mitk::nnUNetTool::GetName() const { return "nnUNet"; } mitk::DataStorage *mitk::nnUNetTool::GetDataStorage() { return this->GetToolManager()->GetDataStorage(); } mitk::DataNode *mitk::nnUNetTool::GetRefNode() { return this->GetToolManager()->GetReferenceData(0); } namespace { void onPythonProcessEvent(itk::Object * /*pCaller*/, const itk::EventObject &e, void *) { std::string testCOUT; std::string testCERR; const auto *pEvent = dynamic_cast(&e); if (pEvent) { testCOUT = testCOUT + pEvent->GetOutput(); MITK_INFO << testCOUT; } const auto *pErrEvent = dynamic_cast(&e); if (pErrEvent) { testCERR = testCERR + pErrEvent->GetOutput(); MITK_ERROR << testCERR; } } } // namespace mitk::LabelSetImage::Pointer mitk::nnUNetTool::ComputeMLPreview(const Image *inputAtTimeStep, TimeStepType /*timeStep*/) { if (m_InputBuffer == inputAtTimeStep) { return m_OutputBuffer; } std::string inDir, outDir, inputImagePath, outputImagePath, scriptPath; ProcessExecutor::Pointer spExec = ProcessExecutor::New(); itk::CStyleCommand::Pointer spCommand = itk::CStyleCommand::New(); spCommand->SetCallback(&onPythonProcessEvent); spExec->AddObserver(ExternalProcessOutputEvent(), spCommand); ProcessExecutor::ArgumentListType args; inDir = IOUtil::CreateTemporaryDirectory("nnunet-in-XXXXXX", this->GetMitkTempDir()); std::ofstream tmpStream; inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, m_TEMPLATE_FILENAME, inDir + IOUtil::GetDirectorySeparator()); tmpStream.close(); std::size_t found = inputImagePath.find_last_of(IOUtil::GetDirectorySeparator()); std::string fileName = inputImagePath.substr(found + 1); std::string token = fileName.substr(0, fileName.find("_")); if (this->GetNoPip()) { scriptPath = this->GetnnUNetDirectory() + IOUtil::GetDirectorySeparator() + "nnunet" + IOUtil::GetDirectorySeparator() + "inference" + IOUtil::GetDirectorySeparator() + "predict_simple.py"; } try { if (this->GetMultiModal()) { const std::string fileFormat(".nii.gz"); const std::string fileNamePart("_000_000"); std::string outModalFile; size_t len = inDir.length() + 1 + token.length() + fileNamePart.length() + 1 + fileFormat.length(); outModalFile.reserve(len); // The 1(s) indicates a directory separator char and an underscore. for (size_t i = 0; i < m_OtherModalPaths.size(); ++i) { mitk::Image::ConstPointer modalImage = m_OtherModalPaths[i]; outModalFile.append(inDir); outModalFile.push_back(IOUtil::GetDirectorySeparator()); outModalFile.append(token); outModalFile.append(fileNamePart); outModalFile.append(std::to_string(i)); outModalFile.append(fileFormat); IOUtil::Save(modalImage.GetPointer(), outModalFile); outModalFile.clear(); } } else { IOUtil::Save(inputAtTimeStep, inputImagePath); } } catch (const mitk::Exception &e) { /* Can't throw mitk exception to the caller. Refer: T28691 */ MITK_ERROR << e.GetDescription(); return nullptr; } // Code calls external process std::string command = "nnUNet_predict"; if (this->GetNoPip()) { #ifdef _WIN32 command = "python"; #else command = "python3"; #endif } for (ModelParams &modelparam : m_ParamQ) { outDir = IOUtil::CreateTemporaryDirectory("nnunet-out-XXXXXX", this->GetMitkTempDir()); outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; modelparam.outputDir = outDir; args.clear(); if (this->GetNoPip()) { args.push_back(scriptPath); } args.push_back("-i"); args.push_back(inDir); args.push_back("-o"); args.push_back(outDir); args.push_back("-t"); args.push_back(modelparam.task); if (modelparam.model.find("cascade") != std::string::npos) { args.push_back("-ctr"); } else { args.push_back("-tr"); } args.push_back(modelparam.trainer); args.push_back("-m"); args.push_back(modelparam.model); args.push_back("-p"); args.push_back(modelparam.planId); if (!modelparam.folds.empty()) { args.push_back("-f"); for (auto fold : modelparam.folds) { args.push_back(fold); } } args.push_back("--num_threads_nifti_save"); args.push_back("1"); // fixing to 1 if (!this->GetMirror()) { args.push_back("--disable_tta"); } if (!this->GetMixedPrecision()) { args.push_back("--disable_mixed_precision"); } if (this->GetEnsemble()) { args.push_back("--save_npz"); } try { std::string resultsFolderEnv = "RESULTS_FOLDER=" + this->GetModelDirectory(); itksys::SystemTools::PutEnv(resultsFolderEnv.c_str()); std::string cudaEnv = "CUDA_VISIBLE_DEVICES=" + std::to_string(this->GetGpuId()); itksys::SystemTools::PutEnv(cudaEnv.c_str()); spExec->Execute(this->GetPythonPath(), command, args); } catch (const mitk::Exception &e) { /* Can't throw mitk exception to the caller. Refer: T28691 */ MITK_ERROR << e.GetDescription(); return nullptr; } } if (this->GetEnsemble() && !this->GetPostProcessingJsonDirectory().empty()) { args.clear(); command = "nnUNet_ensemble"; outDir = IOUtil::CreateTemporaryDirectory("nnunet-ensemble-out-XXXXXX", this->GetMitkTempDir()); outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; args.push_back("-f"); for (ModelParams &modelparam : m_ParamQ) { args.push_back(modelparam.outputDir); } args.push_back("-o"); args.push_back(outDir); if (!this->GetPostProcessingJsonDirectory().empty()) { args.push_back("-pp"); args.push_back(this->GetPostProcessingJsonDirectory()); } spExec->Execute(this->GetPythonPath(), command, args); } try { LabelSetImage::Pointer resultImage = LabelSetImage::New(); Image::Pointer outputImage = IOUtil::Load(outputImagePath); resultImage->InitializeByLabeledImage(outputImage); resultImage->SetGeometry(inputAtTimeStep->GetGeometry()); m_InputBuffer = inputAtTimeStep; + m_OutputBuffer = resultImage; return resultImage; } catch (const mitk::Exception &e) { /* Can't throw mitk exception to the caller. Refer: T28691 */ MITK_ERROR << e.GetDescription(); return nullptr; } } diff --git a/Modules/Segmentation/Interactions/mitknnUnetTool.h b/Modules/Segmentation/Interactions/mitknnUnetTool.h index 9185b0f0a7..7e0fcffd7a 100644 --- a/Modules/Segmentation/Interactions/mitknnUnetTool.h +++ b/Modules/Segmentation/Interactions/mitknnUnetTool.h @@ -1,214 +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. ============================================================================*/ #ifndef mitknnUnetTool_h_Included #define mitknnUnetTool_h_Included #include "mitkAutoMLSegmentationWithPreviewTool.h" #include "mitkCommon.h" #include "mitkToolManager.h" #include #include -#include #include +#include namespace us { class ModuleResource; } namespace mitk { /** * @brief nnUNet parameter request object holding all model parameters for input. * Also holds output temporary directory path. */ struct ModelParams { std::string task; std::vector folds; std::string model; std::string trainer; std::string planId; std::string outputDir; std::string inputName; std::string timeStamp; size_t generateHash() const - { + { std::string toHash; std::string foldsConcatenated = std::accumulate(folds.begin(), folds.end(), std::string("")); toHash += this->task; toHash += this->model; toHash += this->inputName; toHash += foldsConcatenated; toHash += this->timeStamp; size_t hashVal = std::hash{}(toHash); return hashVal; } }; /** \brief nnUNet segmentation tool. \ingroup Interaction \ingroup ToolManagerEtAl \warning Only to be instantiated by mitk::ToolManager. */ class MITKSEGMENTATION_EXPORT nnUNetTool : public AutoMLSegmentationWithPreviewTool { public: mitkClassMacro(nnUNetTool, AutoMLSegmentationWithPreviewTool); itkFactorylessNewMacro(Self); itkCloneMacro(Self); const char **GetXPM() const override; const char *GetName() const override; us::ModuleResource GetIconResource() const override; void Activated() override; itkSetMacro(nnUNetDirectory, std::string); itkGetConstMacro(nnUNetDirectory, std::string); itkSetMacro(ModelDirectory, std::string); itkGetConstMacro(ModelDirectory, std::string); itkSetMacro(PythonPath, std::string); itkGetConstMacro(PythonPath, std::string); itkSetMacro(MitkTempDir, std::string); itkGetConstMacro(MitkTempDir, std::string); itkSetMacro(PostProcessingJsonDirectory, std::string); itkGetConstMacro(PostProcessingJsonDirectory, std::string); itkSetMacro(MixedPrecision, bool); itkGetConstMacro(MixedPrecision, bool); itkBooleanMacro(MixedPrecision); itkSetMacro(Mirror, bool); itkGetConstMacro(Mirror, bool); itkBooleanMacro(Mirror); itkSetMacro(MultiModal, bool); itkGetConstMacro(MultiModal, bool); itkBooleanMacro(MultiModal); itkSetMacro(NoPip, bool); itkGetConstMacro(NoPip, bool); itkBooleanMacro(NoPip); itkSetMacro(Ensemble, bool); itkGetConstMacro(Ensemble, bool); itkBooleanMacro(Ensemble); itkSetMacro(Predict, bool); itkGetConstMacro(Predict, bool); itkBooleanMacro(Predict); itkSetMacro(GpuId, unsigned int); itkGetConstMacro(GpuId, unsigned int); /** * @brief vector of ModelParams. * Size > 1 only for ensemble prediction. */ std::vector m_ParamQ; /** * @brief Holds paths to other input image modalities. * */ std::vector m_OtherModalPaths; mitk::Image::ConstPointer m_InputBuffer; /** * @brief Renders the output LabelSetImage. * To called in the main thread. */ void RenderOutputBuffer(); /** * @brief Get the Output Buffer object * * @return LabelSetImage::Pointer */ LabelSetImage::Pointer GetOutputBuffer(); /** * @brief Sets the outputBuffer to nullptr * */ void ClearOutputBuffer(); /** * @brief Returns the DataStorage from the ToolManager */ mitk::DataStorage *GetDataStorage(); mitk::DataNode *GetRefNode(); - void SetNodeProperties(LabelSetImage::Pointer) override; + void SetOutputBuffer(LabelSetImage::Pointer); protected: /** * @brief Construct a new nnUNet Tool object and temp directory. * */ nnUNetTool(); /** * @brief Destroy the nnUNet Tool object and deletes the temp directory. * */ ~nnUNetTool(); /** * @brief Overriden method from the tool manager to execute the segmentation * Implementation: * 1. Saves the inputAtTimeStep in a temporary directory. * 2. Copies other modalities, renames and saves in the temporary directory, if required. * 3. Sets RESULTS_FOLDER and CUDA_VISIBLE_DEVICES variables in the environment. * 3. Iterates through the parameter queue (m_ParamQ) and executes "nnUNet_predict" command with the parameters * 4. Expects an output image to be saved in the temporary directory by the python proces. Loads it as * LabelSetImage and returns. * * @param inputAtTimeStep * @param timeStep * @return LabelSetImage::Pointer */ LabelSetImage::Pointer ComputeMLPreview(const Image *inputAtTimeStep, TimeStepType timeStep) override; - void UpdateCleanUp() override; private: std::string m_MitkTempDir; std::string m_nnUNetDirectory; std::string m_ModelDirectory; std::string m_PythonPath; std::string m_PostProcessingJsonDirectory; // bool m_UseGPU; kept for future // bool m_AllInGPU; bool m_MixedPrecision; bool m_Mirror; bool m_NoPip; bool m_MultiModal; bool m_Ensemble = false; bool m_Predict; LabelSetImage::Pointer m_OutputBuffer; unsigned int m_GpuId; const std::string m_TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz"; }; } // namespace mitk #endif diff --git a/Modules/Segmentation/Resources/FastMarching_48x48.png b/Modules/Segmentation/Resources/FastMarching_48x48.png deleted file mode 100644 index e67f7a2a75..0000000000 Binary files a/Modules/Segmentation/Resources/FastMarching_48x48.png and /dev/null differ diff --git a/Modules/Segmentation/Resources/FastMarching_Cursor_32x32.png b/Modules/Segmentation/Resources/FastMarching_Cursor_32x32.png deleted file mode 100644 index 12bddbaa4b..0000000000 Binary files a/Modules/Segmentation/Resources/FastMarching_Cursor_32x32.png and /dev/null differ diff --git a/Modules/Segmentation/Resources/Interactions/FastMarchingTool.xml b/Modules/Segmentation/Resources/Interactions/FastMarchingTool.xml deleted file mode 100644 index 1baf80a04b..0000000000 --- a/Modules/Segmentation/Resources/Interactions/FastMarchingTool.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/Modules/Segmentation/Resources/Interactions/PickingTool.xml b/Modules/Segmentation/Resources/Interactions/PickingTool.xml index 1baf80a04b..c87c5441c2 100644 --- a/Modules/Segmentation/Resources/Interactions/PickingTool.xml +++ b/Modules/Segmentation/Resources/Interactions/PickingTool.xml @@ -1,19 +1,19 @@ - + - \ No newline at end of file + diff --git a/Modules/Segmentation/Resources/Watershed_48x48.png b/Modules/Segmentation/Resources/Watershed_48x48.png deleted file mode 100644 index 39641b42ec..0000000000 Binary files a/Modules/Segmentation/Resources/Watershed_48x48.png and /dev/null differ diff --git a/Modules/Segmentation/Resources/Watershed_Cursor_32x32.png b/Modules/Segmentation/Resources/Watershed_Cursor_32x32.png deleted file mode 100644 index 2d88f7f780..0000000000 Binary files a/Modules/Segmentation/Resources/Watershed_Cursor_32x32.png and /dev/null differ diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index b1f29f073e..e8f1ecffb7 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,119 +1,110 @@ set(CPP_FILES Algorithms/mitkCalculateSegmentationVolume.cpp Algorithms/mitkContourModelSetToImageFilter.cpp Algorithms/mitkContourSetToPointSetFilter.cpp Algorithms/mitkContourUtils.cpp Algorithms/mitkCorrectorAlgorithm.cpp Algorithms/mitkDiffImageApplier.cpp Algorithms/mitkDiffSliceOperation.cpp Algorithms/mitkDiffSliceOperationApplier.cpp Algorithms/mitkFeatureBasedEdgeDetectionFilter.cpp Algorithms/mitkImageLiveWireContourModelFilter.cpp Algorithms/mitkImageToContourFilter.cpp #Algorithms/mitkImageToContourModelFilter.cpp Algorithms/mitkImageToLiveWireContourFilter.cpp Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkOtsuSegmentationFilter.cpp Algorithms/mitkSegmentationObjectFactory.cpp Algorithms/mitkShapeBasedInterpolationAlgorithm.cpp Algorithms/mitkShowSegmentationAsSmoothedSurface.cpp Algorithms/mitkShowSegmentationAsSurface.cpp Algorithms/mitkVtkImageOverwrite.cpp Controllers/mitkSegmentationInterpolationController.cpp Controllers/mitkToolManager.cpp Controllers/mitkSegmentationModuleActivator.cpp Controllers/mitkToolManagerProvider.cpp DataManagement/mitkContour.cpp DataManagement/mitkContourSet.cpp DataManagement/mitkExtrudedContour.cpp Interactions/mitkAdaptiveRegionGrowingTool.cpp Interactions/mitkAddContourTool.cpp Interactions/mitkAutoCropTool.cpp Interactions/mitkAutoSegmentationTool.cpp Interactions/mitkAutoSegmentationWithPreviewTool.cpp Interactions/mitkAutoMLSegmentationWithPreviewTool.cpp Interactions/mitkBinaryThresholdBaseTool.cpp Interactions/mitkBinaryThresholdTool.cpp Interactions/mitkBinaryThresholdULTool.cpp Interactions/mitkCalculateGrayValueStatisticsTool.cpp Interactions/mitkCalculateVolumetryTool.cpp Interactions/mitkContourModelInteractor.cpp Interactions/mitkContourModelLiveWireInteractor.cpp Interactions/mitkLiveWireTool2D.cpp Interactions/mitkContourTool.cpp Interactions/mitkCreateSurfaceTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.cpp - Interactions/mitkFastMarchingBaseTool.cpp - Interactions/mitkFastMarchingTool.cpp - Interactions/mitkFastMarchingTool3D.cpp Interactions/mitkFeedbackContourTool.cpp Interactions/mitkFillRegionTool.cpp Interactions/mitkOtsuTool3D.cpp Interactions/mitkPaintbrushTool.cpp Interactions/mitkPixelManipulationTool.cpp Interactions/mitkRegionGrowingTool.cpp Interactions/mitkSegmentationsProcessingTool.cpp Interactions/mitkSetRegionTool.cpp Interactions/mitkSegTool2D.cpp Interactions/mitkSubtractContourTool.cpp Interactions/mitkTool.cpp Interactions/mitkToolCommand.cpp - Interactions/mitkWatershedTool.cpp Interactions/mitkPickingTool.cpp Interactions/mitknnUnetTool.cpp Interactions/mitkSegmentationInteractor.cpp #SO Interactions/mitkProcessExecutor.cpp Rendering/mitkContourMapper2D.cpp Rendering/mitkContourSetMapper2D.cpp Rendering/mitkContourSetVtkMapper3D.cpp Rendering/mitkContourVtkMapper3D.cpp SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp #Added from ML Controllers/mitkSliceBasedInterpolationController.cpp Algorithms/mitkSurfaceStampImageFilter.cpp ) set(RESOURCE_FILES Add_48x48.png Add_Cursor_32x32.png AI_48x48.png AI_Cursor_32x32.png Erase_48x48.png Erase_Cursor_32x32.png - FastMarching_48x48.png - FastMarching_Cursor_32x32.png Fill_48x48.png Fill_Cursor_32x32.png LiveWire_48x48.png LiveWire_Cursor_32x32.png Otsu_48x48.png Paint_48x48.png Paint_Cursor_32x32.png Pick_48x48.png RegionGrowing_48x48.png RegionGrowing_Cursor_32x32.png Subtract_48x48.png Subtract_Cursor_32x32.png Threshold_48x48.png TwoThresholds_48x48.png - Watershed_48x48.png - Watershed_Cursor_32x32.png Wipe_48x48.png Wipe_Cursor_32x32.png Interactions/dummy.xml Interactions/LiveWireTool.xml - Interactions/FastMarchingTool.xml Interactions/PickingTool.xml Interactions/PressMoveRelease.xml Interactions/PressMoveReleaseAndPointSetting.xml Interactions/PressMoveReleaseWithCTRLInversion.xml Interactions/PressMoveReleaseWithCTRLInversionAllMouseMoves.xml Interactions/SegmentationToolsConfig.xml Interactions/ContourModelModificationConfig.xml Interactions/ContourModelModificationInteractor.xml ) diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp index fe9ff8bd3f..2b6853bb74 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp @@ -1,1014 +1,1006 @@ /*============================================================================ 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 "QmitkAdaptiveRegionGrowingToolGUI.h" #include #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include "mitkNodePredicateDataType.h" #include "mitkProperties.h" #include "mitkTransferFunctionProperty.h" #include "mitkImageStatisticsHolder.h" #include "itkMaskImageFilter.h" #include "itkNumericTraits.h" #include #include #include #include #include "QmitkConfirmSegmentationDialog.h" #include "itkOrImageFilter.h" #include "mitkImageCast.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImageCast.h" MITK_TOOL_GUI_MACRO(, QmitkAdaptiveRegionGrowingToolGUI, "") QmitkAdaptiveRegionGrowingToolGUI::QmitkAdaptiveRegionGrowingToolGUI(QWidget *parent) : QmitkToolGUI(), m_DataStorage(nullptr), m_UseVolumeRendering(false), m_UpdateSuggestedThreshold(true), m_SuggestedThValue(0.0) { this->setParent(parent); m_Controls.setupUi(this); m_Controls.m_ThresholdSlider->setDecimals(1); m_Controls.m_ThresholdSlider->setSpinBoxAlignment(Qt::AlignVCenter); m_Controls.m_PreviewSlider->setEnabled(false); m_Controls.m_PreviewSlider->setSingleStep(0.5); // Not yet available // m_Controls.m_PreviewSlider->InvertedAppearance(true); //3D preview doesn't work: T24430. Postponed until reimplementation of segmentation m_Controls.m_cbVolumeRendering->setVisible(false); this->CreateConnections(); this->SetDataNodeNames("labeledRGSegmentation", "RGResult", "RGFeedbackSurface", "maskedSegmentation"); connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); } QmitkAdaptiveRegionGrowingToolGUI::~QmitkAdaptiveRegionGrowingToolGUI() { // Removing the observer of the PointSet node if (m_RegionGrow3DTool->GetPointSetNode().IsNotNull()) { m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetAddObserverTag); m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetMoveObserverTag); } this->RemoveHelperNodes(); } void QmitkAdaptiveRegionGrowingToolGUI::OnNewToolAssociated(mitk::Tool *tool) { m_RegionGrow3DTool = dynamic_cast(tool); if (m_RegionGrow3DTool.IsNotNull()) { SetInputImageNode(this->m_RegionGrow3DTool->GetReferenceData()); this->m_DataStorage = this->m_RegionGrow3DTool->GetDataStorage(); this->EnableControls(true); // Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded); m_PointSetAddObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); m_PointSetMoveObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetMoveEvent(), pointAddedCommand); } else { this->EnableControls(false); } } void QmitkAdaptiveRegionGrowingToolGUI::RemoveHelperNodes() { mitk::DataNode::Pointer imageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (imageNode.IsNotNull()) { m_DataStorage->Remove(imageNode); } mitk::DataNode::Pointer maskedSegmentationNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); if (maskedSegmentationNode.IsNotNull()) { m_DataStorage->Remove(maskedSegmentationNode); } } void QmitkAdaptiveRegionGrowingToolGUI::CreateConnections() { // Connecting GUI components connect((QObject *)(m_Controls.m_pbRunSegmentation), SIGNAL(clicked()), this, SLOT(RunSegmentation())); connect(m_Controls.m_PreviewSlider, SIGNAL(valueChanged(double)), this, SLOT(ChangeLevelWindow(double))); connect((QObject *)(m_Controls.m_pbConfirmSegementation), SIGNAL(clicked()), this, SLOT(ConfirmSegmentation())); connect( m_Controls.m_ThresholdSlider, SIGNAL(maximumValueChanged(double)), this, SLOT(SetUpperThresholdValue(double))); connect( m_Controls.m_ThresholdSlider, SIGNAL(minimumValueChanged(double)), this, SLOT(SetLowerThresholdValue(double))); } void QmitkAdaptiveRegionGrowingToolGUI::SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, std::string surface, std::string maskedSegmentation) { m_NAMEFORLABLEDSEGMENTATIONIMAGE = labledSegmentation; m_NAMEFORBINARYIMAGE = binaryImage; m_NAMEFORSURFACE = surface; m_NAMEFORMASKEDSEGMENTATION = maskedSegmentation; } void QmitkAdaptiveRegionGrowingToolGUI::SetDataStorage(mitk::DataStorage *dataStorage) { m_DataStorage = dataStorage; } void QmitkAdaptiveRegionGrowingToolGUI::SetInputImageNode(mitk::DataNode *node) { m_InputImageNode = node; mitk::Image *inputImage = dynamic_cast(m_InputImageNode->GetData()); if (inputImage) { mitk::ScalarType max = inputImage->GetStatistics()->GetScalarValueMax(); mitk::ScalarType min = inputImage->GetStatistics()->GetScalarValueMin(); m_Controls.m_ThresholdSlider->setMaximum(max); m_Controls.m_ThresholdSlider->setMinimum(min); // Just for initialization m_Controls.m_ThresholdSlider->setMaximumValue(max); m_Controls.m_ThresholdSlider->setMinimumValue(min); } } template static void AccessPixel(mitk::PixelType /*ptype*/, mitk::Image* im, mitk::Point3D p, int& val) { mitk::ImagePixelReadAccessor access(im); val = access.GetPixelByWorldCoordinates(p); } /**Overloaded const verison*/ template static void AccessPixel(mitk::PixelType /*ptype*/, const mitk::Image* im, mitk::Point3D p, int& val) { mitk::ImagePixelReadAccessor access(im); val = access.GetPixelByWorldCoordinates(p); } void QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded() { if (m_RegionGrow3DTool.IsNull()) return; mitk::DataNode *node = m_RegionGrow3DTool->GetPointSetNode(); if (node != nullptr) { mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { QMessageBox::critical(nullptr, "QmitkAdaptiveRegionGrowingToolGUI", "PointSetNode does not contain a pointset"); return; } m_Controls.m_lblSetSeedpoint->setText(""); const mitk::Image *image = dynamic_cast(m_InputImageNode->GetData()); const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); auto image3D = GetImageByTimePoint(image, timePoint); if (nullptr == image3D) { MITK_WARN << "Cannot run segementation. Currently selected timepoint is not in the time bounds of the selected " "reference image. Time point: " << timePoint; return; } if (!pointSet->GetTimeGeometry()->IsValidTimePoint(timePoint)) return; mitk::Point3D seedPoint = pointSet ->GetPointSet(static_cast(pointSet->GetTimeGeometry()->TimePointToTimeStep(timePoint))) ->GetPoints() ->ElementAt(0); if (image3D->GetGeometry()->IsInside(seedPoint)) mitkPixelTypeMultiplex3( AccessPixel, image3D->GetChannelDescriptor().GetPixelType(), image3D, seedPoint, m_SeedpointValue) else return; /* In this case the seedpoint is placed e.g. in the lung or bronchialtree * The lowerFactor sets the windowsize depending on the regiongrowing direction */ m_CurrentRGDirectionIsUpwards = true; if (m_SeedpointValue < -500) { m_CurrentRGDirectionIsUpwards = false; } // Initializing the region by the area around the seedpoint m_SeedPointValueMean = 0; itk::Index<3> currentIndex, runningIndex; mitk::ScalarType pixelValues[125]; unsigned int pos(0); image3D->GetGeometry(0)->WorldToIndex(seedPoint, currentIndex); runningIndex = currentIndex; for (int i = runningIndex[0] - 2; i <= runningIndex[0] + 2; i++) { for (int j = runningIndex[1] - 2; j <= runningIndex[1] + 2; j++) { for (int k = runningIndex[2] - 2; k <= runningIndex[2] + 2; k++) { currentIndex[0] = i; currentIndex[1] = j; currentIndex[2] = k; if (image3D->GetGeometry()->IsIndexInside(currentIndex)) { int component = 0; m_InputImageNode->GetIntProperty("Image.Displayed Component", component); mitkPixelTypeMultiplex4(mitk::FastSinglePixelAccess, image3D->GetChannelDescriptor().GetPixelType(), image3D, nullptr, currentIndex, pixelValues[pos]); pos++; } else { pixelValues[pos] = std::numeric_limits::min(); pos++; } } } } // Now calculation mean of the pixelValues // Now calculation mean of the pixelValues unsigned int numberOfValues(0); for (auto &pixelValue : pixelValues) { if (pixelValue > std::numeric_limits::min()) { m_SeedPointValueMean += pixelValue; numberOfValues++; } } m_SeedPointValueMean = m_SeedPointValueMean / numberOfValues; mitk::ScalarType var = 0; if (numberOfValues > 1) { for (auto &pixelValue : pixelValues) { if (pixelValue > std::numeric_limits::min()) { var += (pixelValue - m_SeedPointValueMean) * (pixelValue - m_SeedPointValueMean); } } var /= numberOfValues - 1; } mitk::ScalarType stdDev = sqrt(var); /* * Here the upper- and lower threshold is calculated: * The windowSize is 20% of the maximum range of the intensity values existing in the current image * If the RG direction is upwards the lower TH is meanSeedValue-0.15*windowSize and upper TH is * meanSeedValue+0.85*windowsSize * if the RG direction is downwards the lower TH is meanSeedValue-0.85*windowSize and upper TH is * meanSeedValue+0.15*windowsSize */ const auto timeStepOfImage = image->GetTimeGeometry()->TimePointToTimeStep(timePoint); mitk::ScalarType min = image->GetStatistics()->GetScalarValueMin(timeStepOfImage); mitk::ScalarType max = image->GetStatistics()->GetScalarValueMax(timeStepOfImage); mitk::ScalarType windowSize = max - min; windowSize = 0.15 * windowSize; if (m_CurrentRGDirectionIsUpwards) { m_LOWERTHRESHOLD = m_SeedPointValueMean - stdDev; m_UPPERTHRESHOLD = m_SeedpointValue + windowSize; if (m_UPPERTHRESHOLD > max) m_UPPERTHRESHOLD = max; m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); } else { m_UPPERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue > m_SeedPointValueMean) m_UPPERTHRESHOLD = m_SeedpointValue; m_LOWERTHRESHOLD = m_SeedpointValue - windowSize; if (m_LOWERTHRESHOLD < min) m_LOWERTHRESHOLD = min; m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); } } } mitk::Image::ConstPointer QmitkAdaptiveRegionGrowingToolGUI::GetImageByTimePoint(const mitk::Image *image, mitk::TimePointType timePoint) const { if (nullptr == image) return image; if (!image->GetTimeGeometry()->IsValidTimePoint(timePoint)) return nullptr; if (image->GetDimension() != 4) return image; auto imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(static_cast(image->GetTimeGeometry()->TimePointToTimeStep(timePoint))); imageTimeSelector->UpdateLargestPossibleRegion(); return imageTimeSelector->GetOutput(); } void QmitkAdaptiveRegionGrowingToolGUI::RunSegmentation() { if (m_InputImageNode.IsNull()) { QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); return; } mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please insert a seed point inside the " "image.\n\nFirst press the \"Define Seed " "Point\" button,\nthen click left mouse " "button inside the image."); return; } // safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast(node->GetData()); if (seedPointSet.IsNull()) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } mitk::Image::Pointer orgImage = dynamic_cast(m_InputImageNode->GetData()); if (orgImage.IsNotNull()) { const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (!seedPointSet->GetTimeGeometry()->IsValidTimePoint(timePoint)) mitkThrow() << "Point set is not defined for specified time point. Time point: " << timePoint; int timeStep = static_cast(seedPointSet->GetTimeGeometry()->TimePointToTimeStep(timePoint)); if (!(seedPointSet->GetSize(timeStep))) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); auto image3D = GetImageByTimePoint(orgImage, timePoint); if (image3D.IsNotNull()) { // QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //set the cursor to waiting AccessByItk_2(image3D, StartRegionGrowing, image3D->GetGeometry(), seedPoint); // QApplication::restoreOverrideCursor();//reset cursor } else { QApplication::restoreOverrideCursor(); // reset cursor QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "Only images of dimension 3 or 4 can be processed!"); return; } } EnableControls(true); // Segmentation ran successfully, so enable all controls. node->SetVisibility(true); QApplication::restoreOverrideCursor(); // reset cursor } template void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(const itk::Image *itkImage, const mitk::BaseGeometry *imageGeometry, const mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::MaskImageFilter MaskImageFilterType; if (!imageGeometry->IsInside(seedPoint)) { QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information(nullptr, "Segmentation functionality", "The seed point is outside of the image! Please choose a position inside the image!"); return; } IndexType seedIndex; imageGeometry->WorldToIndex(seedPoint, seedIndex); // convert world coordinates to image indices if (m_SeedpointValue > m_UPPERTHRESHOLD || m_SeedpointValue < m_LOWERTHRESHOLD) { QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information( nullptr, "Segmentation functionality", "The seed point is outside the defined thresholds! Please set a new seed point or adjust the thresholds."); MITK_INFO << "Mean: " << m_SeedPointValueMean; return; } // Setting the direction of the regiongrowing. For dark structures e.g. the lung the regiongrowing // is performed starting at the upper value going to the lower one regionGrower->SetGrowingDirectionIsUpwards(m_CurrentRGDirectionIsUpwards); regionGrower->SetInput(itkImage); regionGrower->AddSeed(seedIndex); // In some cases we have to subtract 1 for the lower threshold and add 1 to the upper. // Otherwise no region growing is done. Maybe a bug in the ConnectiveAdaptiveThresholdFilter mitk::ScalarType maxPixelValue = m_Controls.m_ThresholdSlider->maximum(); mitk::ScalarType minPixelValue = m_Controls.m_ThresholdSlider->minimum(); if ((m_LOWERTHRESHOLD - minPixelValue) >= 1) { regionGrower->SetLower(m_LOWERTHRESHOLD - 1); } else { regionGrower->SetLower(m_LOWERTHRESHOLD); } if ((maxPixelValue - m_UPPERTHRESHOLD) >= 1) { regionGrower->SetUpper(m_UPPERTHRESHOLD + 1); } else { regionGrower->SetUpper(m_UPPERTHRESHOLD); } try { regionGrower->Update(); } catch (itk::ExceptionObject &exc) { QMessageBox errorInfo; errorInfo.setWindowTitle("Adaptive RG Segmentation Functionality"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during region growing!"); errorInfo.setDetailedText(exc.what()); errorInfo.exec(); return; // can't work } catch (...) { QMessageBox::critical(nullptr, "Adaptive RG Segmentation Functionality", "An error occurred during region growing!"); return; } mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); // initialize slider m_Controls.m_PreviewSlider->setMinimum(m_LOWERTHRESHOLD); mitk::ScalarType max = m_SeedpointValue + resultImage->GetStatistics()->GetScalarValueMax(); if (max < m_UPPERTHRESHOLD) m_Controls.m_PreviewSlider->setMaximum(max); else m_Controls.m_PreviewSlider->setMaximum(m_UPPERTHRESHOLD); this->m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); if (m_CurrentRGDirectionIsUpwards) { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean - 1); } else { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean + 1); } this->m_SliderInitialized = true; // create new node and then delete the old one if there is one mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData(resultImage); // set some properties newNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORLABLEDSEGMENTATIONIMAGE)); newNode->SetProperty("helper object", mitk::BoolProperty::New(true)); newNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); newNode->SetProperty("layer", mitk::IntProperty::New(1)); newNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); // delete the old image, if there was one: mitk::DataNode::Pointer binaryNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); m_DataStorage->Remove(binaryNode); // now add result to data tree m_DataStorage->Add(newNode, m_InputImageNode); typename InputImageType::Pointer inputImageItk; mitk::CastToItkImage(resultImage, inputImageItk); // volume rendering preview masking typename ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); thresholdFilter->SetInput(inputImageItk); thresholdFilter->SetInsideValue(1); thresholdFilter->SetOutsideValue(0); double sliderVal = this->m_Controls.m_PreviewSlider->value(); if (m_CurrentRGDirectionIsUpwards) { thresholdFilter->SetLowerThreshold(sliderVal); thresholdFilter->SetUpperThreshold(itk::NumericTraits::max()); } else { thresholdFilter->SetLowerThreshold(itk::NumericTraits::NonpositiveMin()); thresholdFilter->SetUpperThreshold(sliderVal); } thresholdFilter->SetInPlace(false); typename MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); maskFilter->SetInput(inputImageItk); maskFilter->SetInPlace(false); maskFilter->SetMaskImage(thresholdFilter->GetOutput()); maskFilter->SetOutsideValue(0); maskFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkMask; mitk::CastToMitkImage(maskFilter->GetOutput(), mitkMask); mitk::DataNode::Pointer maskedNode = mitk::DataNode::New(); maskedNode->SetData(mitkMask); // set some properties maskedNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORMASKEDSEGMENTATION)); maskedNode->SetProperty("helper object", mitk::BoolProperty::New(true)); maskedNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); maskedNode->SetProperty("layer", mitk::IntProperty::New(1)); maskedNode->SetProperty("opacity", mitk::FloatProperty::New(0.0)); // delete the old image, if there was one: mitk::DataNode::Pointer deprecatedMask = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); m_DataStorage->Remove(deprecatedMask); // now add result to data tree m_DataStorage->Add(maskedNode, m_InputImageNode); this->InitializeLevelWindow(); if (m_UseVolumeRendering) this->EnableVolumeRendering(true); m_UpdateSuggestedThreshold = true; // reset first stored threshold value // Setting progress to finished mitk::ProgressBar::GetInstance()->Progress(357); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::InitializeLevelWindow() { // get the preview from the datatree mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); mitk::ScalarType *level = new mitk::ScalarType(0.0); mitk::ScalarType *window = new mitk::ScalarType(1.0); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UPPERTHRESHOLD - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LOWERTHRESHOLD; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); // get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = m_UPPERTHRESHOLD - (m_SeedpointValue) + 0.5; } else { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = (m_SeedpointValue)-m_LOWERTHRESHOLD + 0.5; } tempLevelWindow.SetLevelWindow(*level, *window); newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); // update the widgets mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_SliderInitialized = true; // inquiry need to fix bug#1828 static int lastSliderPosition = 0; if ((this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1) == lastSliderPosition) { this->ChangeLevelWindow(lastSliderPosition); } lastSliderPosition = this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1; if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int)(*level + 0.5)); // lower threshold for labeled image } void QmitkAdaptiveRegionGrowingToolGUI::ChangeLevelWindow(double newValue) { if (m_SliderInitialized) { // do nothing, if no preview exists mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); // get the levelWindow associated with the preview mitk::ScalarType level; // = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType *window = new mitk::ScalarType(1); // adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UPPERTHRESHOLD - newValue + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = newValue - m_LOWERTHRESHOLD + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int)(level - 0.5)); // lower threshold for labeled image newNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::DecreaseSlider() { // moves the slider one step to the left, when the "-"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->minimum()) { int newValue = this->m_Controls.m_PreviewSlider->value() - 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::IncreaseSlider() { // moves the slider one step to the right, when the "+"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->maximum()) { int newValue = this->m_Controls.m_PreviewSlider->value() + 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::ConfirmSegmentation() { // get image node if (m_InputImageNode.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "Please specify the image in Datamanager!"); return; } // get image data mitk::Image::Pointer orgImage = dynamic_cast(m_InputImageNode->GetData()); if (orgImage.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Image found!"); return; } // get labeled segmentation mitk::Image::Pointer labeledSeg = (mitk::Image *)m_DataStorage->GetNamedObject(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (labeledSeg.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Segmentation Preview found!"); return; } mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; QmitkConfirmSegmentationDialog dialog; QString segName = QString::fromStdString(m_RegionGrow3DTool->GetCurrentSegmentationName()); dialog.SetSegmentationName(segName); int result = dialog.exec(); switch (result) { case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(false); break; case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(true); break; case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: return; } mitk::Image::Pointer img = dynamic_cast(newNode->GetData()); AccessByItk(img, ITKThresholding); // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); newNode->SetVisibility(false); m_Controls.m_cbVolumeRendering->setChecked(false); // TODO disable slider etc... if (m_RegionGrow3DTool.IsNotNull()) { m_RegionGrow3DTool->ConfirmSegmentation(); } } template void QmitkAdaptiveRegionGrowingToolGUI::ITKThresholding(itk::Image *itkImage) { mitk::Image::Pointer originalSegmentation = dynamic_cast(this->m_RegionGrow3DTool->GetTargetSegmentationNode()->GetData()); const auto timePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); if (!originalSegmentation->GetTimeGeometry()->IsValidTimePoint(timePoint)) mitkThrow() << "Segmentation is not defined for specified time point. Time point: " << timePoint; int timeStep = static_cast(originalSegmentation->GetTimeGeometry()->TimePointToTimeStep(timePoint)); if (originalSegmentation) { typedef itk::Image InputImageType; typedef itk::Image SegmentationType; // select single 3D volume if we have more than one time step typename SegmentationType::Pointer originalSegmentationInITK = SegmentationType::New(); if (originalSegmentation->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(originalSegmentation); timeSelector->SetTimeNr(timeStep); timeSelector->UpdateLargestPossibleRegion(); CastToItkImage(timeSelector->GetOutput(), originalSegmentationInITK); } else // use original { CastToItkImage(originalSegmentation, originalSegmentationInITK); } // Fill current preiview image in segmentation image originalSegmentationInITK->FillBuffer(0); itk::ImageRegionIterator itOutput(originalSegmentationInITK, originalSegmentationInITK->GetLargestPossibleRegion()); itk::ImageRegionIterator itInput(itkImage, itkImage->GetLargestPossibleRegion()); itOutput.GoToBegin(); itInput.GoToBegin(); // calculate threhold from slider value int currentTreshold = 0; if (m_CurrentRGDirectionIsUpwards) { currentTreshold = m_UPPERTHRESHOLD - m_Controls.m_PreviewSlider->value() + 1; } else { currentTreshold = m_Controls.m_PreviewSlider->value() - m_LOWERTHRESHOLD; } // iterate over image and set pixel in segmentation according to thresholded labeled image while (!itOutput.IsAtEnd() && !itInput.IsAtEnd()) { // Use threshold slider to determine if pixel is set to 1 if (itInput.Value() != 0 && itInput.Value() >= static_cast::PixelType>(currentTreshold)) { itOutput.Set(1); } ++itOutput; ++itInput; } // combine current working segmentation image with our region growing result originalSegmentation->SetVolume((void *)(originalSegmentationInITK->GetPixelContainer()->GetBufferPointer()), timeStep); originalSegmentation->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::EnableControls(bool enable) { if (m_RegionGrow3DTool.IsNull()) return; // Check if seed point is already set, if not leave RunSegmentation disabled // if even m_DataStorage is nullptr leave node nullptr mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { this->m_Controls.m_pbRunSegmentation->setEnabled(false); } else { this->m_Controls.m_pbRunSegmentation->setEnabled(enable); } // Check if a segmentation exists, if not leave segmentation dependent disabled. // if even m_DataStorage is nullptr leave node nullptr node = m_DataStorage ? m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE) : nullptr; if (node.IsNull()) { this->m_Controls.m_PreviewSlider->setEnabled(false); this->m_Controls.m_pbConfirmSegementation->setEnabled(false); } else { this->m_Controls.m_PreviewSlider->setEnabled(enable); this->m_Controls.m_pbConfirmSegementation->setEnabled(enable); } this->m_Controls.m_cbVolumeRendering->setEnabled(enable); } void QmitkAdaptiveRegionGrowingToolGUI::EnableVolumeRendering(bool enable) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); if (node.IsNull()) return; - if (enable) - { - node->SetBoolProperty("volumerendering", enable); - node->SetBoolProperty("volumerendering.uselod", true); - } - else - { - node->SetBoolProperty("volumerendering", enable); - } + node->SetBoolProperty("volumerendering", enable); double val = this->m_Controls.m_PreviewSlider->value(); this->ChangeLevelWindow(val); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::UpdateVolumeRenderingThreshold(int) { typedef short PixelType; typedef itk::Image InputImageType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::MaskImageFilter MaskImageFilterType; mitk::DataNode::Pointer grownImageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::Image::Pointer grownImage = dynamic_cast(grownImageNode->GetData()); if (!grownImage) { MITK_ERROR << "Missing data node for labeled segmentation image."; return; } InputImageType::Pointer itkGrownImage; mitk::CastToItkImage(grownImage, itkGrownImage); ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); thresholdFilter->SetInput(itkGrownImage); thresholdFilter->SetInPlace(false); double sliderVal = this->m_Controls.m_PreviewSlider->value(); PixelType threshold = itk::NumericTraits::min(); if (m_CurrentRGDirectionIsUpwards) { threshold = static_cast(m_UPPERTHRESHOLD - sliderVal + 0.5); thresholdFilter->SetLowerThreshold(threshold); thresholdFilter->SetUpperThreshold(itk::NumericTraits::max()); } else { threshold = sliderVal - m_LOWERTHRESHOLD + 0.5; thresholdFilter->SetLowerThreshold(itk::NumericTraits::min()); thresholdFilter->SetUpperThreshold(threshold); } thresholdFilter->UpdateLargestPossibleRegion(); MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); maskFilter->SetInput(itkGrownImage); maskFilter->SetInPlace(false); maskFilter->SetMaskImage(thresholdFilter->GetOutput()); maskFilter->SetOutsideValue(0); maskFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkMaskedImage; mitk::CastToMitkImage(maskFilter->GetOutput(), mitkMaskedImage); mitk::DataNode::Pointer maskNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); maskNode->SetData(mitkMaskedImage); } void QmitkAdaptiveRegionGrowingToolGUI::UseVolumeRendering(bool on) { m_UseVolumeRendering = on; this->EnableVolumeRendering(on); } void QmitkAdaptiveRegionGrowingToolGUI::SetLowerThresholdValue(double lowerThreshold) { m_LOWERTHRESHOLD = lowerThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::SetUpperThresholdValue(double upperThreshold) { m_UPPERTHRESHOLD = upperThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::Deactivated() { // make the segmentation preview node invisible mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (node.IsNotNull()) { node->SetVisibility(false); } // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); m_Controls.m_cbVolumeRendering->setChecked(false); } void QmitkAdaptiveRegionGrowingToolGUI::Activated() { } diff --git a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingTool3DGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkFastMarchingTool3DGUI.cpp deleted file mode 100644 index 165cd8bde0..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingTool3DGUI.cpp +++ /dev/null @@ -1,20 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "QmitkFastMarchingTool3DGUI.h" - -MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkFastMarchingTool3DGUI, "") - -QmitkFastMarchingTool3DGUI::QmitkFastMarchingTool3DGUI() : QmitkFastMarchingToolGUIBase(false) -{ -} - diff --git a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingTool3DGUI.h b/Modules/SegmentationUI/Qmitk/QmitkFastMarchingTool3DGUI.h deleted file mode 100644 index 42f2c60ecb..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingTool3DGUI.h +++ /dev/null @@ -1,38 +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. - -============================================================================*/ - -#ifndef QmitkFastMarchingTool3DGUI_h_Included -#define QmitkFastMarchingTool3DGUI_h_Included - -#include "QmitkFastMarchingToolGUIBase.h" -#include - -/** -\ingroup org_mitk_gui_qt_interactivesegmentation_internal -\brief GUI for mitk::FastMarchingTool3D. -\sa mitk::FastMarchingTool -*/ -class MITKSEGMENTATIONUI_EXPORT QmitkFastMarchingTool3DGUI : public QmitkFastMarchingToolGUIBase -{ - Q_OBJECT - -public: - mitkClassMacro(QmitkFastMarchingTool3DGUI, QmitkFastMarchingToolGUIBase); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - -protected: - QmitkFastMarchingTool3DGUI(); - ~QmitkFastMarchingTool3DGUI() = default; -}; - -#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUI.cpp deleted file mode 100644 index d37040dc8b..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUI.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "QmitkFastMarchingToolGUI.h" - -#include "mitkBaseRenderer.h" -#include "mitkStepper.h" -#include -#include -#include -#include -#include -#include -#include -#include - -MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkFastMarchingToolGUI, "") - -QmitkFastMarchingToolGUI::QmitkFastMarchingToolGUI() : QmitkFastMarchingToolGUIBase(true) -{} diff --git a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUI.h deleted file mode 100644 index 72340f6114..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUI.h +++ /dev/null @@ -1,38 +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. - -============================================================================*/ - -#ifndef QmitkFastMarchingToolGUI_h_Included -#define QmitkFastMarchingToolGUI_h_Included - -#include "QmitkFastMarchingToolGUIBase.h" -#include - -/** -\ingroup org_mitk_gui_qt_interactivesegmentation_internal -\brief GUI for mitk::FastMarchingTool in 2D. -\sa mitk::FastMarchingTool -*/ -class MITKSEGMENTATIONUI_EXPORT QmitkFastMarchingToolGUI : public QmitkFastMarchingToolGUIBase -{ - Q_OBJECT - -public: - mitkClassMacro(QmitkFastMarchingToolGUI, QmitkFastMarchingToolGUIBase); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - -protected: - QmitkFastMarchingToolGUI(); - ~QmitkFastMarchingToolGUI() = default; -}; - -#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUIBase.cpp deleted file mode 100644 index 1bd272c0f5..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUIBase.cpp +++ /dev/null @@ -1,296 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "QmitkFastMarchingToolGUIBase.h" - -#include "mitkBaseRenderer.h" -#include "mitkStepper.h" -#include -#include -#include -#include -#include -#include -#include -#include - -QmitkFastMarchingToolGUIBase::QmitkFastMarchingToolGUIBase(bool mode2D) : QmitkAutoSegmentationToolGUIBase(mode2D) -{ -} - -QmitkFastMarchingToolGUIBase::~QmitkFastMarchingToolGUIBase() -{ -} - -void QmitkFastMarchingToolGUIBase::Update() -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - tool->SetLowerThreshold(m_slwThreshold->minimumValue()); - tool->SetUpperThreshold(m_slwThreshold->maximumValue()); - tool->SetStoppingValue(m_slStoppingValue->value()); - tool->SetSigma(m_slSigma->value()); - tool->SetAlpha(m_slAlpha->value()); - tool->SetBeta(m_slBeta->value()); - tool->UpdatePreview(); - } -} - -void QmitkFastMarchingToolGUIBase::OnThresholdChanged(double lower, double upper) -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - tool->SetLowerThreshold(lower); - tool->SetUpperThreshold(upper); - this->Update(); - } -} - -void QmitkFastMarchingToolGUIBase::OnBetaChanged(double value) -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - tool->SetBeta(value); - this->Update(); - } -} - -void QmitkFastMarchingToolGUIBase::OnSigmaChanged(double value) -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - tool->SetSigma(value); - this->Update(); - } -} - -void QmitkFastMarchingToolGUIBase::OnAlphaChanged(double value) -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - tool->SetAlpha(value); - this->Update(); - } -} - -void QmitkFastMarchingToolGUIBase::OnStoppingValueChanged(double value) -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - tool->SetStoppingValue(value); - this->Update(); - } -} - -void QmitkFastMarchingToolGUIBase::OnClearSeeds() -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - tool->ClearSeeds(); - this->EnableWidgets(false); - this->Update(); - } -} - -void QmitkFastMarchingToolGUIBase::ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool) -{ - Superclass::ConnectNewTool(newTool); - - auto tool = dynamic_cast(newTool); - if (nullptr != tool) - { - tool->SetLowerThreshold(m_slwThreshold->minimumValue()); - tool->SetUpperThreshold(m_slwThreshold->maximumValue()); - tool->SetStoppingValue(m_slStoppingValue->value()); - tool->SetSigma(m_slSigma->value()); - tool->SetAlpha(m_slAlpha->value()); - tool->SetBeta(m_slBeta->value()); - tool->ClearSeeds(); - } -} - -void QmitkFastMarchingToolGUIBase::InitializeUI(QBoxLayout* mainLayout) -{ - mainLayout->setContentsMargins(0, 0, 0, 0); - - QFont fntHelp; - fntHelp.setBold(true); - - QLabel* lblHelp = new QLabel(this); - lblHelp->setText("Press shift-click to add seeds repeatedly."); - lblHelp->setFont(fntHelp); - - mainLayout->addWidget(lblHelp); - - // Sigma controls - { - QHBoxLayout* hlayout = new QHBoxLayout(); - hlayout->setSpacing(2); - - QLabel* lbl = new QLabel(this); - lbl->setText("Sigma: "); - hlayout->addWidget(lbl); - - QSpacerItem* sp2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - hlayout->addItem(sp2); - - mainLayout->addItem(hlayout); - } - - m_slSigma = new ctkSliderWidget(this); - m_slSigma->setMinimum(0.1); - m_slSigma->setMaximum(5.0); - m_slSigma->setPageStep(0.1); - m_slSigma->setSingleStep(0.01); - m_slSigma->setValue(1.0); - m_slSigma->setTracking(false); - m_slSigma->setToolTip("The \"sigma\" parameter in the Gradient Magnitude filter."); - connect(m_slSigma, SIGNAL(valueChanged(double)), this, SLOT(OnSigmaChanged(double))); - mainLayout->addWidget(m_slSigma); - - // Alpha controls - { - QHBoxLayout* hlayout = new QHBoxLayout(); - hlayout->setSpacing(2); - - QLabel* lbl = new QLabel(this); - lbl->setText("Alpha: "); - hlayout->addWidget(lbl); - - QSpacerItem* sp2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - hlayout->addItem(sp2); - - mainLayout->addItem(hlayout); - } - - m_slAlpha = new ctkSliderWidget(this); - m_slAlpha->setMinimum(-10); - m_slAlpha->setMaximum(0); - m_slAlpha->setPageStep(0.1); - m_slAlpha->setSingleStep(0.01); - m_slAlpha->setValue(-2.5); - m_slAlpha->setTracking(false); - m_slAlpha->setToolTip("The \"alpha\" parameter in the Sigmoid mapping filter."); - connect(m_slAlpha, SIGNAL(valueChanged(double)), this, SLOT(OnAlphaChanged(double))); - mainLayout->addWidget(m_slAlpha); - - // Beta controls - { - QHBoxLayout* hlayout = new QHBoxLayout(); - hlayout->setSpacing(2); - - QLabel* lbl = new QLabel(this); - lbl->setText("Beta: "); - hlayout->addWidget(lbl); - - QSpacerItem* sp2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - hlayout->addItem(sp2); - - mainLayout->addLayout(hlayout); - } - - m_slBeta = new ctkSliderWidget(this); - m_slBeta->setMinimum(0); - m_slBeta->setMaximum(100); - m_slBeta->setPageStep(0.1); - m_slBeta->setSingleStep(0.01); - m_slBeta->setValue(3.5); - m_slBeta->setTracking(false); - m_slBeta->setToolTip("The \"beta\" parameter in the Sigmoid mapping filter."); - connect(m_slBeta, SIGNAL(valueChanged(double)), this, SLOT(OnBetaChanged(double))); - mainLayout->addWidget(m_slBeta); - - // stopping value controls - { - QHBoxLayout* hlayout = new QHBoxLayout(); - hlayout->setSpacing(2); - - QLabel* lbl = new QLabel(this); - lbl->setText("Stopping value: "); - hlayout->addWidget(lbl); - - QSpacerItem* sp2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - hlayout->addItem(sp2); - - mainLayout->addLayout(hlayout); - } - - m_slStoppingValue = new ctkSliderWidget(this); - m_slStoppingValue->setMinimum(0); - m_slStoppingValue->setMaximum(10000); - m_slStoppingValue->setPageStep(10); - m_slStoppingValue->setSingleStep(1); - m_slStoppingValue->setValue(2000); - m_slStoppingValue->setDecimals(0); - m_slStoppingValue->setTracking(false); - m_slStoppingValue->setToolTip("The \"stopping value\" parameter in the fast marching 3D algorithm"); - connect(m_slStoppingValue, SIGNAL(valueChanged(double)), this, SLOT(OnStoppingValueChanged(double))); - mainLayout->addWidget(m_slStoppingValue); - - // threshold controls - { - QHBoxLayout* hlayout = new QHBoxLayout(); - hlayout->setSpacing(2); - - QLabel* lbl = new QLabel(this); - lbl->setText("Threshold: "); - hlayout->addWidget(lbl); - - QSpacerItem* sp2 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum); - hlayout->addItem(sp2); - - mainLayout->addLayout(hlayout); - } - - m_slwThreshold = new ctkRangeWidget(this); - m_slwThreshold->setMinimum(-100); - m_slwThreshold->setMaximum(5000); - m_slwThreshold->setMinimumValue(-100); - m_slwThreshold->setMaximumValue(2000); - m_slwThreshold->setDecimals(0); - m_slwThreshold->setTracking(false); - m_slwThreshold->setToolTip("The lower and upper thresholds for the final thresholding"); - connect(m_slwThreshold, SIGNAL(valuesChanged(double, double)), this, SLOT(OnThresholdChanged(double, double))); - mainLayout->addWidget(m_slwThreshold); - - m_btClearSeeds = new QPushButton("Clear"); - m_btClearSeeds->setToolTip("Clear current result and start over again"); - m_btClearSeeds->setEnabled(false); - mainLayout->addWidget(m_btClearSeeds); - connect(m_btClearSeeds, SIGNAL(clicked()), this, SLOT(OnClearSeeds())); - - m_slSigma->setDecimals(2); - m_slBeta->setDecimals(2); - m_slAlpha->setDecimals(2); - - this->EnableWidgets(false); - - Superclass::InitializeUI(mainLayout); -} - -void QmitkFastMarchingToolGUIBase::EnableWidgets(bool enable) -{ - Superclass::EnableWidgets(enable); - m_slSigma->setEnabled(enable); - m_slAlpha->setEnabled(enable); - m_slBeta->setEnabled(enable); - m_slStoppingValue->setEnabled(enable); - m_slwThreshold->setEnabled(enable); - m_btClearSeeds->setEnabled(enable); -} diff --git a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUIBase.h deleted file mode 100644 index 669ef27155..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkFastMarchingToolGUIBase.h +++ /dev/null @@ -1,67 +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. - -============================================================================*/ - -#ifndef QmitkFastMarchingToolGUIBase_h_Included -#define QmitkFastMarchingToolGUIBase_h_Included - -#include "QmitkAutoSegmentationToolGUIBase.h" -#include "mitkFastMarchingTool3D.h" -#include - -class ctkSliderWidget; -class ctkRangeWidget; -class QPushButton; - -/** -\ingroup org_mitk_gui_qt_interactivesegmentation_internal -\brief Base GUI for mitk::FastMarchingTool (2D and 3D). -\sa mitk::FastMarchingTool -*/ -class MITKSEGMENTATIONUI_EXPORT QmitkFastMarchingToolGUIBase : public QmitkAutoSegmentationToolGUIBase -{ - Q_OBJECT - -public: - mitkClassMacro(QmitkFastMarchingToolGUIBase, QmitkAutoSegmentationToolGUIBase); - -protected slots: - - void OnThresholdChanged(double, double); - void OnAlphaChanged(double); - void OnBetaChanged(double); - void OnSigmaChanged(double); - void OnStoppingValueChanged(double); - void OnClearSeeds(); - -protected: - QmitkFastMarchingToolGUIBase(bool mode2D); - ~QmitkFastMarchingToolGUIBase() override; - - void ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool) override; - void InitializeUI(QBoxLayout* mainLayout) override; - - void EnableWidgets(bool) override; - - void Update(); - - ctkRangeWidget *m_slwThreshold; - ctkSliderWidget *m_slStoppingValue; - ctkSliderWidget *m_slSigma; - ctkSliderWidget *m_slAlpha; - ctkSliderWidget *m_slBeta; - - QPushButton *m_btClearSeeds; - -private: -}; - -#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp index df55ce1800..09a09a2dd9 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkLabelSetWidget.cpp @@ -1,1295 +1,1291 @@ /*============================================================================ 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 "QmitkLabelSetWidget.h" // mitk #include #include #include #include #include #include #include #include #include #include #include // Qmitk #include #include #include // Qt #include #include #include #include #include #include #include #include #include // itk #include -// todo: -// berry -//#include - QmitkLabelSetWidget::QmitkLabelSetWidget(QWidget *parent) : QWidget(parent), m_DataStorage(nullptr), m_Completer(nullptr), m_ToolManager(nullptr), m_ProcessingManualSelection(false) { m_Controls.setupUi(this); m_ColorSequenceRainbow.GoToBegin(); m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_Controls.m_LabelSearchBox->setAlwaysShowClearIcon(true); m_Controls.m_LabelSearchBox->setShowSearchIcon(true); QStringList completionList; completionList << ""; m_Completer = new QCompleter(completionList, this); m_Completer->setCaseSensitivity(Qt::CaseInsensitive); m_Controls.m_LabelSearchBox->setCompleter(m_Completer); connect(m_Controls.m_LabelSearchBox, SIGNAL(returnPressed()), this, SLOT(OnSearchLabel())); QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(GetLabelStringList()); m_Controls.m_LabelSearchBox->setEnabled(false); m_Controls.m_lblCaption->setText(""); InitializeTableWidget(); } QmitkLabelSetWidget::~QmitkLabelSetWidget() {} void QmitkLabelSetWidget::OnTableViewContextMenuRequested(const QPoint & /*pos*/) { int pixelValue = GetPixelValueOfSelectedItem(); if (-1 == pixelValue) return; QMenu *menu = new QMenu(m_Controls.m_LabelSetTableWidget); if (m_Controls.m_LabelSetTableWidget->selectedItems().size() > 1) { QAction *mergeAction = new QAction(QIcon(":/Qmitk/MergeLabels.png"), "Merge selection on current label", this); mergeAction->setEnabled(true); QObject::connect(mergeAction, SIGNAL(triggered(bool)), this, SLOT(OnMergeLabels(bool))); menu->addAction(mergeAction); QAction *removeLabelsAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove selected labels", this); removeLabelsAction->setEnabled(true); QObject::connect(removeLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabels(bool))); menu->addAction(removeLabelsAction); QAction *eraseLabelsAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase selected labels", this); eraseLabelsAction->setEnabled(true); QObject::connect(eraseLabelsAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabels(bool))); menu->addAction(eraseLabelsAction); /*QAction* combineAndCreateSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Combine and create a surface", this); combineAndCreateSurfaceAction->setEnabled(true); QObject::connect( combineAndCreateSurfaceAction, SIGNAL(triggered(bool)), this, SLOT(OnCombineAndCreateSurface(bool)));*/ // menu->addAction(combineAndCreateSurfaceAction); Not implemented /*QAction* createMasksAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create a mask for each selected label", this); createMasksAction->setEnabled(true); QObject::connect(createMasksAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMasks(bool)));*/ // menu->addAction(createMasksAction); Not implemented /*QAction* combineAndCreateMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Combine and create a mask", this); combineAndCreateMaskAction->setEnabled(true); QObject::connect(combineAndCreateMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCombineAndCreateMask(bool)));*/ // menu->addAction(combineAndCreateMaskAction); Not implemented } else { QAction *renameAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Rename...", this); renameAction->setEnabled(true); QObject::connect(renameAction, SIGNAL(triggered(bool)), this, SLOT(OnRenameLabel(bool))); menu->addAction(renameAction); QAction *removeAction = new QAction(QIcon(":/Qmitk/RemoveLabel.png"), "Remove...", this); removeAction->setEnabled(true); QObject::connect(removeAction, SIGNAL(triggered(bool)), this, SLOT(OnRemoveLabel(bool))); menu->addAction(removeAction); QAction *eraseAction = new QAction(QIcon(":/Qmitk/EraseLabel.png"), "Erase...", this); eraseAction->setEnabled(true); QObject::connect(eraseAction, SIGNAL(triggered(bool)), this, SLOT(OnEraseLabel(bool))); menu->addAction(eraseAction); QAction *randomColorAction = new QAction(QIcon(":/Qmitk/RandomColor.png"), "Random color", this); randomColorAction->setEnabled(true); QObject::connect(randomColorAction, SIGNAL(triggered(bool)), this, SLOT(OnRandomColor(bool))); menu->addAction(randomColorAction); QAction *viewOnlyAction = new QAction(QIcon(":/Qmitk/visible.png"), "View only", this); viewOnlyAction->setEnabled(true); QObject::connect(viewOnlyAction, SIGNAL(triggered(bool)), this, SLOT(OnSetOnlyActiveLabelVisible(bool))); menu->addAction(viewOnlyAction); QAction *viewAllAction = new QAction(QIcon(":/Qmitk/visible.png"), "View all", this); viewAllAction->setEnabled(true); QObject::connect(viewAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsVisible(bool))); menu->addAction(viewAllAction); QAction *hideAllAction = new QAction(QIcon(":/Qmitk/invisible.png"), "Hide all", this); hideAllAction->setEnabled(true); QObject::connect(hideAllAction, SIGNAL(triggered(bool)), this, SLOT(OnSetAllLabelsInvisible(bool))); menu->addAction(hideAllAction); QAction *lockAllAction = new QAction(QIcon(":/Qmitk/lock.png"), "Lock all", this); lockAllAction->setEnabled(true); QObject::connect(lockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnLockAllLabels(bool))); menu->addAction(lockAllAction); QAction *unlockAllAction = new QAction(QIcon(":/Qmitk/unlock.png"), "Unlock all", this); unlockAllAction->setEnabled(true); QObject::connect(unlockAllAction, SIGNAL(triggered(bool)), this, SLOT(OnUnlockAllLabels(bool))); menu->addAction(unlockAllAction); QAction *createSurfaceAction = new QAction(QIcon(":/Qmitk/CreateSurface.png"), "Create surface", this); createSurfaceAction->setEnabled(true); createSurfaceAction->setMenu(new QMenu()); QAction *tmp1 = createSurfaceAction->menu()->addAction(QString("Detailed")); QAction *tmp2 = createSurfaceAction->menu()->addAction(QString("Smoothed")); QObject::connect(tmp1, SIGNAL(triggered(bool)), this, SLOT(OnCreateDetailedSurface(bool))); QObject::connect(tmp2, SIGNAL(triggered(bool)), this, SLOT(OnCreateSmoothedSurface(bool))); menu->addAction(createSurfaceAction); QAction *createMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create mask", this); createMaskAction->setEnabled(true); QObject::connect(createMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateMask(bool))); menu->addAction(createMaskAction); QAction *createCroppedMaskAction = new QAction(QIcon(":/Qmitk/CreateMask.png"), "Create cropped mask", this); createCroppedMaskAction->setEnabled(true); QObject::connect(createCroppedMaskAction, SIGNAL(triggered(bool)), this, SLOT(OnCreateCroppedMask(bool))); // QAction* importAction = new QAction(QIcon(":/Qmitk/RenameLabel.png"), "Import...", this ); // importAction->setEnabled(true); // QObject::connect( importAction, SIGNAL( triggered(bool) ), this, SLOT( OnImportSegmentationSession(bool) ) ); // menu->addAction(importAction); menu->addAction(createCroppedMaskAction); QSlider *opacitySlider = new QSlider; opacitySlider->setMinimum(0); opacitySlider->setMaximum(100); opacitySlider->setOrientation(Qt::Horizontal); QObject::connect(opacitySlider, SIGNAL(valueChanged(int)), this, SLOT(OnOpacityChanged(int))); QLabel *_OpacityLabel = new QLabel("Opacity: "); QVBoxLayout *_OpacityWidgetLayout = new QVBoxLayout; _OpacityWidgetLayout->setContentsMargins(4, 4, 4, 4); _OpacityWidgetLayout->addWidget(_OpacityLabel); _OpacityWidgetLayout->addWidget(opacitySlider); QWidget *_OpacityWidget = new QWidget; _OpacityWidget->setLayout(_OpacityWidgetLayout); QWidgetAction *OpacityAction = new QWidgetAction(this); OpacityAction->setDefaultWidget(_OpacityWidget); // QObject::connect( m_OpacityAction, SIGNAL( changed() ), this, SLOT( OpacityActionChanged() ) ); auto workingImage = this->GetWorkingImage(); auto activeLayer = workingImage->GetActiveLayer(); auto label = workingImage->GetLabel(pixelValue, activeLayer); if (nullptr != label) { auto opacity = label->GetOpacity(); opacitySlider->setValue(static_cast(opacity * 100)); } menu->addAction(OpacityAction); } menu->popup(QCursor::pos()); } void QmitkLabelSetWidget::OnUnlockAllLabels(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(false); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnLockAllLabels(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsLocked(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnSetAllLabelsVisible(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(true); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnSetAllLabelsInvisible(bool /*value*/) { GetWorkingImage()->GetActiveLabelSet()->SetAllLabelsVisible(false); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnSetOnlyActiveLabelVisible(bool /*value*/) { mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); workingImage->GetActiveLabelSet()->SetAllLabelsVisible(false); workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->SetVisible(true); workingImage->GetActiveLabelSet()->UpdateLookupTable(pixelValue); this->WaitCursorOn(); const mitk::Point3D &pos = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates(); this->WaitCursorOff(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnEraseLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to erase the contents of label \""; question.append( QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question(this, "Erase label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { this->WaitCursorOn(); GetWorkingImage()->EraseLabel(pixelValue); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnRemoveLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to remove label \""; question.append( QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question(this, "Remove label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { this->WaitCursorOn(); GetWorkingImage()->GetActiveLabelSet()->RemoveLabel(pixelValue); GetWorkingImage()->EraseLabel(pixelValue); this->WaitCursorOff(); } ResetAllTableWidgetItems(); } void QmitkLabelSetWidget::OnRenameLabel(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QmitkNewSegmentationDialog dialog(this); dialog.setWindowTitle("Rename Label"); dialog.SetSuggestionList(m_OrganColors); dialog.SetColor(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetColor()); dialog.SetSegmentationName( QString::fromStdString(GetWorkingImage()->GetActiveLabelSet()->GetLabel(pixelValue)->GetName())); if (dialog.exec() == QDialog::Rejected) { return; } QString segmentationName = dialog.GetSegmentationName(); if (segmentationName.isEmpty()) { segmentationName = "Unnamed"; } GetWorkingImage()->GetActiveLabelSet()->RenameLabel(pixelValue, segmentationName.toStdString(), dialog.GetColor()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::OnCombineAndCreateMask(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ...to do... // } void QmitkLabelSetWidget::OnCreateMasks(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ..to do.. // } void QmitkLabelSetWidget::OnCombineAndCreateSurface(bool /*value*/) { m_Controls.m_LabelSetTableWidget->selectedRanges(); // ..to do.. // } void QmitkLabelSetWidget::OnEraseLabels(bool /*value*/) { QString question = "Do you really want to erase the selected labels?"; QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Erase selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) return; std::vector VectorOfLablePixelValues; foreach (QTableWidgetSelectionRange a, ranges) for (int i = a.topRow(); i <= a.bottomRow(); i++) VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); this->WaitCursorOn(); GetWorkingImage()->EraseLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnRemoveLabels(bool /*value*/) { - QString question = "Do you really want to remove selected labels?"; + QString question = "Do you really want to remove the selected labels?"; QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Remove selected labels", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) { return; } std::vector VectorOfLablePixelValues; foreach (QTableWidgetSelectionRange a, ranges) { for (int i = a.topRow(); i <= a.bottomRow(); ++i) { VectorOfLablePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); } } this->WaitCursorOn(); GetWorkingImage()->RemoveLabels(VectorOfLablePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); } ResetAllTableWidgetItems(); } void QmitkLabelSetWidget::OnMergeLabels(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); QString question = "Do you really want to merge selected labels into \""; question.append( QString::fromStdString(GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetName())); question.append("\"?"); QMessageBox::StandardButton answerButton = QMessageBox::question( this, "Merge selected label", question, QMessageBox::Yes | QMessageBox::Cancel, QMessageBox::Yes); if (answerButton == QMessageBox::Yes) { QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (ranges.isEmpty()) { return; } std::vector vectorOfSourcePixelValues; foreach (QTableWidgetSelectionRange a, ranges) { for (int i = a.topRow(); i <= a.bottomRow(); ++i) { vectorOfSourcePixelValues.push_back(m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt()); } } this->WaitCursorOn(); GetWorkingImage()->MergeLabels(pixelValue, vectorOfSourcePixelValues, GetWorkingImage()->GetActiveLayer()); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnLockedButtonClicked() { int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, LOCKED_COL)) { row = i; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt(); GetWorkingImage() ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer()) ->SetLocked(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetLocked()); } } void QmitkLabelSetWidget::OnVisibleButtonClicked() { int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, VISIBLE_COL)) { row = i; break; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { QTableWidgetItem *item = m_Controls.m_LabelSetTableWidget->item(row, 0); int pixelValue = item->data(Qt::UserRole).toInt(); GetWorkingImage() ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer()) ->SetVisible(!GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetVisible()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnColorButtonClicked() { int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (sender() == m_Controls.m_LabelSetTableWidget->cellWidget(i, COLOR_COL)) { row = i; } } if (row >= 0 && row < m_Controls.m_LabelSetTableWidget->rowCount()) { int pixelValue = m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt(); const mitk::Color &color = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetColor(); QColor initial(color.GetRed() * 255, color.GetGreen() * 255, color.GetBlue() * 255); QColor qcolor = QColorDialog::getColor(initial, nullptr, QString("Change color")); if (!qcolor.isValid()) { return; } QPushButton *button = static_cast(m_Controls.m_LabelSetTableWidget->cellWidget(row, COLOR_COL)); if (!button) { return; } button->setAutoFillBackground(true); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(qcolor.red())); styleSheet.append(","); styleSheet.append(QString::number(qcolor.green())); styleSheet.append(","); styleSheet.append(QString::number(qcolor.blue())); styleSheet.append("); border: 0;"); button->setStyleSheet(styleSheet); mitk::Color newColor; newColor.SetRed(qcolor.red() / 255.0); newColor.SetGreen(qcolor.green() / 255.0); newColor.SetBlue(qcolor.blue() / 255.0); GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetColor(newColor); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } } void QmitkLabelSetWidget::OnRandomColor(bool /*value*/) { int pixelValue = GetPixelValueOfSelectedItem(); GetWorkingImage() ->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer()) ->SetColor(m_ColorSequenceRainbow.GetNextColor()); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); UpdateAllTableWidgetItems(); } void QmitkLabelSetWidget::SetOrganColors(const QStringList &organColors) { m_OrganColors = organColors; } void QmitkLabelSetWidget::OnActiveLabelChanged(int pixelValue) { mitk::LabelSetImage *workingImage = GetWorkingImage(); assert(workingImage); workingImage->GetActiveLabelSet()->SetActiveLabel(pixelValue); // MITK_INFO << "Active Label set to << " << pixelValue; mitk::SurfaceBasedInterpolationController *interpolator = mitk::SurfaceBasedInterpolationController::GetInstance(); if (interpolator) { interpolator->SetActiveLabel(pixelValue); } workingImage->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnItemClicked(QTableWidgetItem *item) { if (!item) return; int pixelValue = item->data(Qt::UserRole).toInt(); QList ranges = m_Controls.m_LabelSetTableWidget->selectedRanges(); if (!ranges.empty() && ranges.back().rowCount() == 1) { m_ProcessingManualSelection = true; OnActiveLabelChanged(pixelValue); m_ProcessingManualSelection = false; mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkLabelSetWidget::OnItemDoubleClicked(QTableWidgetItem *item) { if (!item) return; int pixelValue = item->data(Qt::UserRole).toInt(); // OnItemClicked(item); <<-- Double click first call OnItemClicked WaitCursorOn(); mitk::LabelSetImage *workingImage = GetWorkingImage(); workingImage->UpdateCenterOfMass(pixelValue, workingImage->GetActiveLayer()); const mitk::Point3D &pos = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetCenterOfMassCoordinates(); WaitCursorOff(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } workingImage->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::SelectLabelByPixelValue(mitk::Label::PixelType pixelValue) { if (m_ProcessingManualSelection || !GetWorkingImage()->ExistLabel(pixelValue)) return; for (int row = 0; row < m_Controls.m_LabelSetTableWidget->rowCount(); row++) { if (m_Controls.m_LabelSetTableWidget->item(row, 0)->data(Qt::UserRole).toInt() == pixelValue) { m_Controls.m_LabelSetTableWidget->clearSelection(); m_Controls.m_LabelSetTableWidget->selectRow(row); m_Controls.m_LabelSetTableWidget->scrollToItem(m_Controls.m_LabelSetTableWidget->item(row, 0)); return; } } } void QmitkLabelSetWidget::InsertTableWidgetItem(mitk::Label *label) { const mitk::Color &color = label->GetColor(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[1] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[2] * 255)); styleSheet.append("); border: 0;"); QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2; QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); QTableWidgetItem *nameItem = new QTableWidgetItem(text); nameItem->setTextAlignment(Qt::AlignCenter | Qt::AlignLeft); // ---!--- // IMPORTANT: ADD PIXELVALUE TO TABLEWIDGETITEM.DATA nameItem->setData(Qt::UserRole, QVariant(label->GetValue())); // ---!--- QPushButton *pbColor = new QPushButton(tableWidget); pbColor->setFixedSize(24, 24); pbColor->setCheckable(false); pbColor->setAutoFillBackground(true); pbColor->setToolTip("Change label color"); pbColor->setStyleSheet(styleSheet); connect(pbColor, SIGNAL(clicked()), this, SLOT(OnColorButtonClicked())); QString transparentStyleSheet = QLatin1String("background-color: transparent; border: 0;"); QPushButton *pbLocked = new QPushButton(tableWidget); pbLocked->setFixedSize(24, 24); QIcon *iconLocked = new QIcon(); auto lockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/lock.svg")); auto unlockIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/unlock.svg")); iconLocked->addPixmap(lockIcon.pixmap(64), QIcon::Normal, QIcon::Off); iconLocked->addPixmap(unlockIcon.pixmap(64), QIcon::Normal, QIcon::On); pbLocked->setIcon(*iconLocked); pbLocked->setIconSize(QSize(24, 24)); pbLocked->setCheckable(true); pbLocked->setToolTip("Lock/unlock label"); pbLocked->setChecked(!label->GetLocked()); pbLocked->setStyleSheet(transparentStyleSheet); connect(pbLocked, SIGNAL(clicked()), this, SLOT(OnLockedButtonClicked())); QPushButton *pbVisible = new QPushButton(tableWidget); pbVisible->setFixedSize(24, 24); pbVisible->setAutoRepeat(false); QIcon *iconVisible = new QIcon(); auto visibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/visible.svg")); auto invisibleIcon = QmitkStyleManager::ThemeIcon(QLatin1String(":/Qmitk/invisible.svg")); iconVisible->addPixmap(visibleIcon.pixmap(64), QIcon::Normal, QIcon::Off); iconVisible->addPixmap(invisibleIcon.pixmap(64), QIcon::Normal, QIcon::On); pbVisible->setIcon(*iconVisible); pbVisible->setIconSize(QSize(24, 24)); pbVisible->setCheckable(true); pbVisible->setToolTip("Show/hide label"); pbVisible->setChecked(!label->GetVisible()); pbVisible->setStyleSheet(transparentStyleSheet); connect(pbVisible, SIGNAL(clicked()), this, SLOT(OnVisibleButtonClicked())); int row = tableWidget->rowCount(); tableWidget->insertRow(row); tableWidget->setRowHeight(row, 24); tableWidget->setItem(row, 0, nameItem); tableWidget->setCellWidget(row, 1, pbLocked); tableWidget->setCellWidget(row, 2, pbColor); tableWidget->setCellWidget(row, 3, pbVisible); tableWidget->selectRow(row); // m_LabelSetImage->SetActiveLabel(label->GetPixelValue()); // m_ToolManager->WorkingDataModified.Send(); // emit activeLabelChanged(label->GetPixelValue()); if (row == 0) { tableWidget->hideRow(row); // hide exterior label } } void QmitkLabelSetWidget::UpdateAllTableWidgetItems() { mitk::LabelSetImage *workingImage = GetWorkingImage(); if (!workingImage) return; // add all labels QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; m_LabelStringList.clear(); for (int i = 0; i < tableWidget->rowCount(); ++i) { UpdateTableWidgetItem(tableWidget->item(i, 0)); m_LabelStringList.append(tableWidget->item(i, 0)->text()); } OnLabelListModified(m_LabelStringList); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::UpdateTableWidgetItem(QTableWidgetItem *item) { mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Label *label = workingImage->GetLabel(item->data(Qt::UserRole).toInt(), workingImage->GetActiveLayer()); const mitk::Color &color = label->GetColor(); QString styleSheet = "background-color:rgb("; styleSheet.append(QString::number(color[0] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[1] * 255)); styleSheet.append(","); styleSheet.append(QString::number(color[2] * 255)); styleSheet.append("); border: 0;"); QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; int colWidth = (tableWidget->columnWidth(NAME_COL) < 180) ? 180 : tableWidget->columnWidth(NAME_COL) - 2; QString text = fontMetrics().elidedText(label->GetName().c_str(), Qt::ElideMiddle, colWidth); item->setText(text); QPushButton *pbLocked = dynamic_cast(tableWidget->cellWidget(item->row(), 1)); pbLocked->setChecked(!label->GetLocked()); QPushButton *pbColor = dynamic_cast(tableWidget->cellWidget(item->row(), 2)); pbColor->setStyleSheet(styleSheet); QPushButton *pbVisible = dynamic_cast(tableWidget->cellWidget(item->row(), 3)); pbVisible->setChecked(!label->GetVisible()); if (item->row() == 0) { tableWidget->hideRow(item->row()); // hide exterior label } } void QmitkLabelSetWidget::ResetAllTableWidgetItems() { QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; // remove all rows while (tableWidget->rowCount()) { tableWidget->removeRow(0); } mitk::DataNode * workingNode = GetWorkingNode(); auto workingImage = dynamic_cast(workingNode->GetData()); if (nullptr == workingImage) { return; } // add all labels m_LabelStringList.clear(); mitk::LabelSet::LabelContainerConstIteratorType it = workingImage->GetActiveLabelSet()->IteratorConstBegin(); mitk::LabelSet::LabelContainerConstIteratorType end = workingImage->GetActiveLabelSet()->IteratorConstEnd(); int pixelValue = -1; while (it != end) { InsertTableWidgetItem(it->second); if (workingImage->GetActiveLabel() == it->second) // get active pixelValue = it->first; m_LabelStringList.append(QString(it->second->GetName().c_str())); it++; } SelectLabelByPixelValue(pixelValue); OnLabelListModified(m_LabelStringList); std::stringstream captionText; captionText << "Number of labels: " << workingImage->GetNumberOfLabels(workingImage->GetActiveLayer()) - 1; m_Controls.m_lblCaption->setText(captionText.str().c_str()); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); emit LabelSetWidgetReset(); } int QmitkLabelSetWidget::GetPixelValueOfSelectedItem() { if (m_Controls.m_LabelSetTableWidget->currentItem()) { return m_Controls.m_LabelSetTableWidget->currentItem()->data(Qt::UserRole).toInt(); } return -1; } QStringList &QmitkLabelSetWidget::GetLabelStringList() { return m_LabelStringList; } void QmitkLabelSetWidget::InitializeTableWidget() { QTableWidget *tableWidget = m_Controls.m_LabelSetTableWidget; tableWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Maximum); tableWidget->setTabKeyNavigation(false); tableWidget->setAlternatingRowColors(false); tableWidget->setFocusPolicy(Qt::NoFocus); tableWidget->setColumnCount(4); tableWidget->resizeColumnToContents(NAME_COL); tableWidget->setColumnWidth(LOCKED_COL, 25); tableWidget->setColumnWidth(COLOR_COL, 25); tableWidget->setColumnWidth(VISIBLE_COL, 25); tableWidget->horizontalHeader()->setSectionResizeMode(0, QHeaderView::Stretch); tableWidget->setContextMenuPolicy(Qt::CustomContextMenu); tableWidget->horizontalHeader()->hide(); tableWidget->setSortingEnabled(false); tableWidget->verticalHeader()->hide(); tableWidget->setEditTriggers(QAbstractItemView::NoEditTriggers); connect(tableWidget, SIGNAL(itemClicked(QTableWidgetItem *)), this, SLOT(OnItemClicked(QTableWidgetItem *))); connect( tableWidget, SIGNAL(itemDoubleClicked(QTableWidgetItem *)), this, SLOT(OnItemDoubleClicked(QTableWidgetItem *))); connect(tableWidget, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(OnTableViewContextMenuRequested(const QPoint &))); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(activeLabelChanged(int)), this, SLOT(OnActiveLabelChanged(int)) // ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importSegmentation()), this, SLOT( OnImportSegmentation()) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(importLabeledImage()), this, SLOT( OnImportLabeledImage()) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(renameLabel(int, const mitk::Color&, const std::string&)), this, // SLOT(OnRenameLabel(int, const mitk::Color&, const std::string&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createSurface(int, bool)), this, SLOT(OnCreateSurface(int, bool)) // ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(toggleOutline(bool)), this, SLOT(OnToggleOutline(bool)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(goToLabel(const mitk::Point3D&)), this, SIGNAL(goToLabel(const // mitk::Point3D&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateSurface( const QList& // )), // this, SLOT(OnCombineAndCreateSurface( const QList&)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createMask(int)), this, SLOT(OnCreateMask(int)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(createCroppedMask(int)), this, SLOT(OnCreateCroppedMask(int)) ); // connect( m_Controls.m_LabelSetTableWidget, SIGNAL(combineAndCreateMask( const QList& // )), // this, SLOT(OnCombineAndCreateMask( const QList&)) ); } void QmitkLabelSetWidget::OnOpacityChanged(int value) { int pixelValue = GetPixelValueOfSelectedItem(); float opacity = static_cast(value) / 100.0f; GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->SetOpacity(opacity); GetWorkingImage()->GetActiveLabelSet()->UpdateLookupTable(pixelValue); } void QmitkLabelSetWidget::setEnabled(bool enabled) { QWidget::setEnabled(enabled); UpdateControls(); } void QmitkLabelSetWidget::SetDataStorage(mitk::DataStorage *storage) { m_DataStorage = storage; } void QmitkLabelSetWidget::OnSearchLabel() { std::string text = m_Controls.m_LabelSearchBox->text().toStdString(); int pixelValue = -1; int row = -1; for (int i = 0; i < m_Controls.m_LabelSetTableWidget->rowCount(); ++i) { if (m_Controls.m_LabelSetTableWidget->item(i, 0)->text().toStdString().compare(text) == 0) { pixelValue = m_Controls.m_LabelSetTableWidget->item(i, 0)->data(Qt::UserRole).toInt(); row = i; break; } } if (pixelValue == -1) { return; } GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue); QTableWidgetItem *nameItem = m_Controls.m_LabelSetTableWidget->item(row, NAME_COL); if (!nameItem) { return; } m_Controls.m_LabelSetTableWidget->clearSelection(); m_Controls.m_LabelSetTableWidget->selectRow(row); m_Controls.m_LabelSetTableWidget->scrollToItem(nameItem); GetWorkingImage()->GetActiveLabelSet()->SetActiveLabel(pixelValue); this->WaitCursorOn(); mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates(); m_ToolManager->WorkingDataChanged(); if (pos.GetVnlVector().max_value() > 0.0) { emit goToLabel(pos); } else { GetWorkingImage()->UpdateCenterOfMass(pixelValue, GetWorkingImage()->GetActiveLayer()); mitk::Point3D pos = GetWorkingImage()->GetLabel(pixelValue, GetWorkingImage()->GetActiveLayer())->GetCenterOfMassCoordinates(); emit goToLabel(pos); } this->WaitCursorOff(); } void QmitkLabelSetWidget::OnLabelListModified(const QStringList &list) { QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(list); } mitk::LabelSetImage *QmitkLabelSetWidget::GetWorkingImage() { mitk::DataNode *workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = dynamic_cast(workingNode->GetData()); assert(workingImage); return workingImage; } mitk::DataNode *QmitkLabelSetWidget::GetWorkingNode() { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); return workingNode; } void QmitkLabelSetWidget::UpdateControls() { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); bool hasWorkingData = (workingNode != nullptr); m_Controls.m_LabelSetTableWidget->setEnabled(hasWorkingData); m_Controls.m_LabelSearchBox->setEnabled(hasWorkingData); if (!hasWorkingData) return; QStringListModel *completeModel = static_cast(m_Completer->model()); completeModel->setStringList(GetLabelStringList()); } void QmitkLabelSetWidget::OnCreateCroppedMask(bool) { m_ToolManager->ActivateTool(-1); mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Image::Pointer maskImage; int pixelValue = GetPixelValueOfSelectedItem(); try { this->WaitCursorOn(); mitk::AutoCropImageFilter::Pointer cropFilter = mitk::AutoCropImageFilter::New(); cropFilter->SetInput(workingImage->CreateLabelMask(pixelValue)); cropFilter->SetBackgroundValue(0); cropFilter->SetMarginFactor(1.15); cropFilter->Update(); maskImage = cropFilter->GetOutput(); this->WaitCursorOff(); } catch (mitk::Exception &e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } if (maskImage.IsNull()) { QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); name += "-mask"; maskNode->SetName(name); maskNode->SetData(maskImage); maskNode->SetBoolProperty("binary", true); maskNode->SetBoolProperty("outline binary", true); maskNode->SetBoolProperty("outline binary shadow", true); maskNode->SetFloatProperty("outline width", 2.0); maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor()); maskNode->SetOpacity(1.0); m_DataStorage->Add(maskNode, GetWorkingNode()); } void QmitkLabelSetWidget::OnCreateMask(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::LabelSetImage *workingImage = GetWorkingImage(); mitk::Image::Pointer maskImage; int pixelValue = GetPixelValueOfSelectedItem(); try { this->WaitCursorOn(); maskImage = workingImage->CreateLabelMask(pixelValue); this->WaitCursorOff(); } catch (mitk::Exception &e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } if (maskImage.IsNull()) { QMessageBox::information(this, "Create Mask", "Could not create a mask out of the selected label.\n"); return; } mitk::DataNode::Pointer maskNode = mitk::DataNode::New(); std::string name = workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetName(); name += "-mask"; maskNode->SetName(name); maskNode->SetData(maskImage); maskNode->SetBoolProperty("binary", true); maskNode->SetBoolProperty("outline binary", true); maskNode->SetBoolProperty("outline binary shadow", true); maskNode->SetFloatProperty("outline width", 2.0); maskNode->SetColor(workingImage->GetLabel(pixelValue, workingImage->GetActiveLayer())->GetColor()); maskNode->SetOpacity(1.0); m_DataStorage->Add(maskNode, GetWorkingNode()); } void QmitkLabelSetWidget::OnToggleOutline(bool value) { mitk::DataNode *workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); workingNode->SetBoolProperty("labelset.contour.active", value); workingNode->GetData()->Modified(); // fixme: workaround to force data-type rendering (and not only property-type) mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::OnCreateSmoothedSurface(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::DataNode::Pointer workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); mitk::DataNode::Pointer groupNode = workingNode; surfaceFilter->SetPointerParameter("Group node", groupNode); surfaceFilter->SetPointerParameter("Input", workingImage); surfaceFilter->SetParameter("RequestedLabel", pixelValue); surfaceFilter->SetParameter("Smooth", true); surfaceFilter->SetDataStorage(*m_DataStorage); mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); try { surfaceFilter->StartAlgorithm(); } catch (mitk::Exception &e) { MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); } } void QmitkLabelSetWidget::OnCreateDetailedSurface(bool /*triggered*/) { m_ToolManager->ActivateTool(-1); mitk::DataNode::Pointer workingNode = GetWorkingNode(); mitk::LabelSetImage *workingImage = GetWorkingImage(); int pixelValue = GetPixelValueOfSelectedItem(); mitk::LabelSetImageToSurfaceThreadedFilter::Pointer surfaceFilter = mitk::LabelSetImageToSurfaceThreadedFilter::New(); itk::SimpleMemberCommand::Pointer successCommand = itk::SimpleMemberCommand::New(); successCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ResultAvailable(), successCommand); itk::SimpleMemberCommand::Pointer errorCommand = itk::SimpleMemberCommand::New(); errorCommand->SetCallbackFunction(this, &QmitkLabelSetWidget::OnThreadedCalculationDone); surfaceFilter->AddObserver(mitk::ProcessingError(), errorCommand); mitk::DataNode::Pointer groupNode = workingNode; surfaceFilter->SetPointerParameter("Group node", groupNode); surfaceFilter->SetPointerParameter("Input", workingImage); surfaceFilter->SetParameter("RequestedLabel", pixelValue); surfaceFilter->SetParameter("Smooth", false); surfaceFilter->SetDataStorage(*m_DataStorage); mitk::StatusBar::GetInstance()->DisplayText("Surface creation is running in background..."); try { surfaceFilter->StartAlgorithm(); } catch (mitk::Exception &e) { MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Create Surface", "Could not create a surface mesh out of the selected label. See error log for details.\n"); } } void QmitkLabelSetWidget::OnImportLabeledImage() { /* m_ToolManager->ActivateTool(-1); mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); assert(referenceNode); // Ask the user for a list of files to open QStringList fileNames = QFileDialog::getOpenFileNames( this, "Open Image", m_LastFileOpenPath, mitk::CoreObjectFactory::GetInstance()->GetFileExtensions()); if (fileNames.empty()) return; try { this->WaitCursorOn(); mitk::Image::Pointer image = mitk::IOUtil::Load( fileNames.front().toStdString() ); if (image.IsNull()) { this->WaitCursorOff(); QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation.\n"); return; } mitk::LabelSetImage::Pointer newImage = mitk::LabelSetImage::New(); newImage->InitializeByLabeledImage(image); this->WaitCursorOff(); mitk::DataNode::Pointer newNode = mitk::DataNode::New(); std::string newName = referenceNode->GetName(); newName += "-labels"; newNode->SetName(newName); newNode->SetData(newImage); m_DataStorage->Add(newNode, referenceNode); } catch (mitk::Exception & e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Import Labeled Image", "Could not load the selected segmentation. See error log for details.\n"); return; } this->UpdateControls(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); */ } void QmitkLabelSetWidget::OnImportSegmentation() { /* m_ToolManager->ActivateTool(-1); mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); assert(workingNode); mitk::LabelSetImage* workingImage = dynamic_cast( workingNode->GetData() ); assert(workingImage); std::string fileExtensions("Segmentation files (*.lset);;"); QString qfileName = QFileDialog::getOpenFileName(this, "Import Segmentation", m_LastFileOpenPath, fileExtensions.c_str() ); if (qfileName.isEmpty() ) return; mitk::NrrdLabelSetImageReader::Pointer reader = mitk::NrrdLabelSetImageReader::New(); reader->SetFileName(qfileName.toLatin1()); try { this->WaitCursorOn(); reader->Update(); mitk::LabelSetImage::Pointer newImage = reader->GetOutput(); workingImage->Concatenate(newImage); this->WaitCursorOff(); } catch ( mitk::Exception& e ) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::information(this, "Import Segmentation", "Could not import the selected segmentation session.\n See error log for details.\n"); } */ mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkLabelSetWidget::WaitCursorOn() { QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); } void QmitkLabelSetWidget::WaitCursorOff() { this->RestoreOverrideCursor(); } void QmitkLabelSetWidget::RestoreOverrideCursor() { QApplication::restoreOverrideCursor(); } void QmitkLabelSetWidget::OnThreadedCalculationDone() { mitk::StatusBar::GetInstance()->Clear(); } diff --git a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp deleted file mode 100644 index fda699f8d4..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.cpp +++ /dev/null @@ -1,139 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "QmitkWatershedToolGUI.h" -#include "mitkWatershedTool.h" - -#include - -MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkWatershedToolGUI, "") - -QmitkWatershedToolGUI::QmitkWatershedToolGUI() : QmitkAutoMLSegmentationToolGUIBase() -{ -} - -void QmitkWatershedToolGUI::ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool) -{ - Superclass::ConnectNewTool(newTool); - - auto tool = dynamic_cast(newTool); - if (nullptr != tool) - { - tool->SetLevel(m_Level); - tool->SetThreshold(m_Threshold); - } - - newTool->IsTimePointChangeAwareOff(); -} - -void QmitkWatershedToolGUI::InitializeUI(QBoxLayout* mainLayout) -{ - m_Controls.setupUi(this); - - m_Controls.thresholdSlider->setMinimum(0); - //We set the threshold maximum to 0.5 to avoid crashes in the watershed filter - //see T27703 for more details. - m_Controls.thresholdSlider->setMaximum(0.5); - m_Controls.thresholdSlider->setValue(m_Threshold); - m_Controls.thresholdSlider->setPageStep(0.01); - m_Controls.thresholdSlider->setSingleStep(0.001); - m_Controls.thresholdSlider->setDecimals(4); - - m_Controls.levelSlider->setMinimum(0); - m_Controls.levelSlider->setMaximum(1); - m_Controls.levelSlider->setValue(m_Level); - m_Controls.levelSlider->setPageStep(0.1); - m_Controls.levelSlider->setSingleStep(0.01); - - connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnSettingsAccept())); - connect(m_Controls.levelSlider, SIGNAL(valueChanged(double)), this, SLOT(OnLevelChanged(double))); - connect(m_Controls.thresholdSlider, SIGNAL(valueChanged(double)), this, SLOT(OnThresholdChanged(double))); - - mainLayout->addLayout(m_Controls.verticalLayout); - - Superclass::InitializeUI(mainLayout); -} - -void QmitkWatershedToolGUI::OnSettingsAccept() -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - try - { - m_Threshold = m_Controls.thresholdSlider->value(); - m_Level = m_Controls.levelSlider->value(); - tool->SetThreshold(m_Threshold); - tool->SetLevel(m_Level); - - tool->UpdatePreview(); - } - catch (const std::exception& e) - { - this->setCursor(Qt::ArrowCursor); - std::stringstream stream; - stream << "Error while generation watershed segmentation. Reason: " << e.what(); - - QMessageBox* messageBox = - new QMessageBox(QMessageBox::Critical, - nullptr, stream.str().c_str()); - messageBox->exec(); - delete messageBox; - MITK_ERROR << stream.str(); - return; - } - catch (...) - { - this->setCursor(Qt::ArrowCursor); - std::stringstream stream; - stream << "Unkown error occured while generation watershed segmentation."; - - QMessageBox* messageBox = - new QMessageBox(QMessageBox::Critical, - nullptr, stream.str().c_str()); - messageBox->exec(); - delete messageBox; - MITK_ERROR << stream.str(); - return; - } - - this->SetLabelSetPreview(tool->GetMLPreview()); - tool->IsTimePointChangeAwareOn(); - } -} - -void QmitkWatershedToolGUI::EnableWidgets(bool enabled) -{ - Superclass::EnableWidgets(enabled); - m_Controls.levelSlider->setEnabled(enabled); - m_Controls.thresholdSlider->setEnabled(enabled); - m_Controls.previewButton->setEnabled(enabled); -} - - -void QmitkWatershedToolGUI::OnLevelChanged(double value) -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - tool->SetLevel(value); - } -} - -void QmitkWatershedToolGUI::OnThresholdChanged(double value) -{ - auto tool = this->GetConnectedToolAs(); - if (nullptr != tool) - { - tool->SetThreshold(value); - } -} diff --git a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.h deleted file mode 100644 index 193257e01f..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUI.h +++ /dev/null @@ -1,66 +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. - -============================================================================*/ - -#ifndef QmitkWatershedToolGUI_h_Included -#define QmitkWatershedToolGUI_h_Included - -#include "QmitkAutoMLSegmentationToolGUIBase.h" - -#include "ui_QmitkWatershedToolGUIControls.h" - -#include - -/** - \ingroup org_mitk_gui_qt_interactivesegmentation_internal - \brief GUI for mitk::WatershedTool. - \sa mitk::WatershedTool - - This GUI shows two sliders to change the watershed parameters. It executes the watershed algorithm by clicking on the - button. - -*/ -class MITKSEGMENTATIONUI_EXPORT QmitkWatershedToolGUI : public QmitkAutoMLSegmentationToolGUIBase -{ - Q_OBJECT - -public: - mitkClassMacro(QmitkWatershedToolGUI, QmitkAutoMLSegmentationToolGUIBase); - itkFactorylessNewMacro(Self); - itkCloneMacro(Self); - -protected slots : - - void OnSettingsAccept(); - - void OnLevelChanged(double value); - void OnThresholdChanged(double value); - -protected: - QmitkWatershedToolGUI(); - ~QmitkWatershedToolGUI() = default; - - void ConnectNewTool(mitk::AutoSegmentationWithPreviewTool* newTool) override; - void InitializeUI(QBoxLayout* mainLayout) override; - - void EnableWidgets(bool enabled) override; - - //Recommendation from ITK is to have a threshold:level ration around 1:100 - //we set Level a bit higher. This provokes more oversegmentation, - //but produces less objects in the first run and profits form the fact that - //decreasing level is quite fast in the filter. - double m_Level = 0.6; - double m_Threshold = 0.004; - - Ui_QmitkWatershedToolGUIControls m_Controls; -}; - -#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUIControls.ui deleted file mode 100644 index 4ce970d215..0000000000 --- a/Modules/SegmentationUI/Qmitk/QmitkWatershedToolGUIControls.ui +++ /dev/null @@ -1,109 +0,0 @@ - - - QmitkWatershedToolGUIControls - - - - 0 - 0 - 192 - 352 - - - - - 0 - 0 - - - - - 100 - 0 - - - - - 100000 - 100000 - - - - QmitkOtsuToolWidget - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - Level: - - - - - - - - 0 - 0 - - - - Threshold: - - - - - - - - - - - - - - - - 0 - 0 - - - - - 100000 - 16777215 - - - - Preview - - - - - - - - - ctkSliderWidget - QWidget -
ctkSliderWidget.h
- 1 -
-
- - -
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h index d005f62eb3..50e188bd8f 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetFolderParser.h @@ -1,281 +1,301 @@ /*============================================================================ 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.s ============================================================================*/ #ifndef QmitknnUNetFolderParser_h_Included #define QmitknnUNetFolderParser_h_Included #include "QmitknnUNetToolGUI.h" #include #include #include /** * @brief Struct to store each (Folder) Node of the hierarchy tree structure. * */ struct FolderNode { QString name; QString path; // parent std::vector> subFolders; }; /** * @brief Class to store and retreive folder hierarchy information * of RESULTS_FOLDER. Only Root node is explicitly stored in m_RootNode. * No. of sub levels in the hierachry is defined in the LEVEL constant. * */ class MITKSEGMENTATIONUI_EXPORT QmitknnUNetFolderParser { public: /** * @brief Construct a new QmitknnUNetFolderParser object * Initializes root folder node object pointer calls * @param parentFolder */ QmitknnUNetFolderParser(const QString parentFolder) { m_RootNode = std::make_shared(); m_RootNode->path = parentFolder; m_RootNode->name = QString("nnUNet"); m_RootNode->subFolders.clear(); InitDirs(m_RootNode, 0); } /** * @brief Destroy the QmitknnUNetFolderParser object * */ ~QmitknnUNetFolderParser() = default; /*{ DeleteDirs(m_RootNode, LEVEL); }*/ /** * @brief Returns the "Results Folder" string which is parent path of the root node. * * @return QString */ QString getResultsFolder() { return m_RootNode->path; } /** * @brief Returns the Model Names from root node. Template function, * type can be any of stl or Qt containers which supports push_back call. * * @tparam T * @return T (any of stl or Qt containers which supports push_back call) */ template T getModelNames() { auto models = GetSubFolderNamesFromNode(m_RootNode); return models; } /** * @brief Returns the task names for a given model. Template function, * type can be any of stl or Qt containers which supports push_back call. * * @tparam T * @param modelName * @return T (any of stl or Qt containers which supports push_back call) */ template T getTasksForModel(const QString &modelName) { std::shared_ptr modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode); auto tasks = GetSubFolderNamesFromNode(modelNode); return tasks; } /** * @brief Returns the models names for a given task. Template function, * type can be any of stl or Qt containers which supports push_back call. * * @tparam T * @param taskName * @return T (any of stl or Qt containers which supports push_back call) */ template T getModelsForTask(const QString &taskName) { T modelsForTask; auto models = GetSubFolderNamesFromNode(m_RootNode); foreach (QString model, models) { QStringList taskList = getTasksForModel(model); if (taskList.contains(taskName, Qt::CaseInsensitive)) { modelsForTask << model; } } return modelsForTask; } + /** + * @brief Returns all the task names present in the root node with possible duplicates. + * Template function, type can be any of stl or Qt containers which supports push_back call. + * + * @tparam T + * @param taskName + * @return T (any of stl or Qt containers which supports push_back call) + */ + template + T getAllTasks() + { + T allTasks; + auto models = GetSubFolderNamesFromNode(m_RootNode); + foreach (QString model, models) + { + allTasks << getTasksForModel(model); + } + return allTasks; + } + /** * @brief Returns the trainer / planner names for a given task & model. Template function, * type can be any of stl or Qt containers which supports push_back call. * * @tparam T * @param taskName * @param modelName * @return T (any of stl or Qt containers which supports push_back call) */ template T getTrainerPlannersForTask(const QString &taskName, const QString &modelName) { std::shared_ptr modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode); std::shared_ptr taskNode = GetSubNodeMatchingNameCrietria(taskName, modelNode); auto tps = GetSubFolderNamesFromNode(taskNode); return tps; } /** * @brief Returns the Folds names for a given trainer,planner,task & model name. Template function, * type can be any of stl or Qt containers which supports push_back call. * * @tparam T * @param trainer * @param planner * @param taskName * @param modelName * @return T (any of stl or Qt containers which supports push_back call) */ template T getFoldsForTrainerPlanner(const QString &trainer, const QString &planner, const QString &taskName, const QString &modelName) { std::shared_ptr modelNode = GetSubNodeMatchingNameCrietria(modelName, m_RootNode); std::shared_ptr taskNode = GetSubNodeMatchingNameCrietria(taskName, modelNode); QString trainerPlanner = trainer + QString("__") + planner; std::shared_ptr tpNode = GetSubNodeMatchingNameCrietria(trainerPlanner, taskNode); auto folds = GetSubFolderNamesFromNode(tpNode); return folds; } private: const int m_LEVEL = 4; std::shared_ptr m_RootNode; /** * @brief Iterates through the root node and returns the sub FolderNode object Matching Name Crietria * * @param queryName * @param parentNode * @return std::shared_ptr */ std::shared_ptr GetSubNodeMatchingNameCrietria(const QString &queryName, std::shared_ptr parentNode) { std::shared_ptr retNode; std::vector> subNodes = parentNode->subFolders; for (std::shared_ptr node : subNodes) { if (node->name == queryName) { retNode = node; break; } } return retNode; } /** * @brief Returns the sub folder names for a folder node object. Template function, * type can be any of stl or Qt containers which supports push_back call. * * @tparam T * @param std::shared_ptr * @return T (any of stl or Qt containers which supports push_back call) */ template T GetSubFolderNamesFromNode(const std::shared_ptr parent) { T folders; std::vector> subNodes = parent->subFolders; for (std::shared_ptr folder : subNodes) { folders.push_back(folder->name); } return folders; } /** * @brief Iterates through the sub folder hierarchy upto a level provided * and create a tree structure. * * @param parent * @param level */ void InitDirs(std::shared_ptr parent, int level) { QString searchFolder = parent->path + QDir::separator() + parent->name; auto subFolders = FetchFoldersFromDir(searchFolder); level++; foreach (QString folder, subFolders) { std::shared_ptr fp = std::make_shared(); fp->path = searchFolder; fp->name = folder; if (level < this->m_LEVEL) { InitDirs(fp, level); } parent->subFolders.push_back(fp); } } /** * @brief Iterates through the sub folder hierarchy upto a level provided * and clears the sub folder std::vector from each node. * * @param parent * @param level */ void DeleteDirs(std::shared_ptr parent, int level) { level++; for (std::shared_ptr subFolder : parent->subFolders) { if (level < m_LEVEL) { DeleteDirs(subFolder, level); } parent->subFolders.clear(); } } /** * @brief Template function to fetch all folders inside a given path. * The type can be any of stl or Qt containers which supports push_back call. * * @tparam T * @param path * @return T */ template T FetchFoldersFromDir(const QString &path) { T folders; for (QDirIterator it(path, QDir::AllDirs, QDirIterator::NoIteratorFlags); it.hasNext();) { it.next(); if (!it.fileName().startsWith('.')) { folders.push_back(it.fileName()); } } return folders; } }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp index bdbb5651e4..29114ac8f8 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.cpp @@ -1,498 +1,588 @@ /*============================================================================ 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 "QmitknnUNetToolGUI.h" +#include "mitkProcessExecutor.h" #include "mitknnUnetTool.h" #include #include #include +#include +#include #include #include +#include #include MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitknnUNetToolGUI, "") QmitknnUNetToolGUI::QmitknnUNetToolGUI() : QmitkAutoMLSegmentationToolGUIBase() { // Nvidia-smi command returning zero doesn't always imply lack of GPUs. // Pytorch uses its own libraries to communicate to the GPUs. Hence, only a warning can be given. if (m_GpuLoader.GetGPUCount() == 0) { std::string warning = "WARNING: No GPUs were detected on your machine. The nnUNet tool might not work."; ShowErrorMessage(warning); } // define predicates for multi modal data selection combobox auto imageType = mitk::TNodePredicateDataType::New(); auto labelSetImageType = mitk::NodePredicateNot::New(mitk::TNodePredicateDataType::New()); m_MultiModalPredicate = mitk::NodePredicateAnd::New(imageType, labelSetImageType).GetPointer(); } void QmitknnUNetToolGUI::ConnectNewTool(mitk::AutoSegmentationWithPreviewTool *newTool) { Superclass::ConnectNewTool(newTool); newTool->IsTimePointChangeAwareOff(); } void QmitknnUNetToolGUI::InitializeUI(QBoxLayout *mainLayout) { m_Controls.setupUi(this); #ifndef _WIN32 m_Controls.pythonEnvComboBox->addItem("/usr/bin"); #endif m_Controls.pythonEnvComboBox->addItem("Select"); AutoParsePythonPaths(); SetGPUInfo(); connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewRequested())); connect(m_Controls.modeldirectoryBox, SIGNAL(directoryChanged(const QString &)), this, SLOT(OnDirectoryChanged(const QString &))); connect( m_Controls.modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &))); connect(m_Controls.taskBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTaskChanged(const QString &))); connect( m_Controls.plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &))); - connect(m_Controls.nopipBox, SIGNAL(stateChanged(int)), this, SLOT(OnCheckBoxChanged(int))); connect(m_Controls.multiModalBox, SIGNAL(stateChanged(int)), this, SLOT(OnCheckBoxChanged(int))); connect(m_Controls.multiModalSpinBox, SIGNAL(valueChanged(int)), this, SLOT(OnModalitiesNumberChanged(int))); connect(m_Controls.pythonEnvComboBox, #if QT_VERSION >= 0x050F00 // 5.15 SIGNAL(textActivated(const QString &)), #elif QT_VERSION >= 0x050C00 // 5.12 - SIGNAL(activated(const QString &)), + SIGNAL(currentTextChanged(const QString &)), #endif this, SLOT(OnPythonPathChanged(const QString &))); connect(m_Controls.refreshdirectoryBox, SIGNAL(clicked()), this, SLOT(OnRefreshPresssed())); connect(m_Controls.clearCacheButton, SIGNAL(clicked()), this, SLOT(OnClearCachePressed())); - m_Controls.codedirectoryBox->setVisible(false); - m_Controls.nnUnetdirLabel->setVisible(false); m_Controls.multiModalSpinBox->setVisible(false); + m_Controls.multiModalSpinBox->setEnabled(false); m_Controls.multiModalSpinLabel->setVisible(false); m_Controls.previewButton->setEnabled(false); - m_Controls.stopButton->setVisible(false); // Hidden for future - QIcon refreshIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/view-refresh.svg")); m_Controls.refreshdirectoryBox->setIcon(refreshIcon); QIcon dirIcon = QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-open.svg")); m_Controls.modeldirectoryBox->setIcon(dirIcon); m_Controls.refreshdirectoryBox->setEnabled(true); m_Controls.statusLabel->setTextFormat(Qt::RichText); if (m_GpuLoader.GetGPUCount() != 0) { WriteStatusMessage(QString("STATUS: Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected.")); } else { WriteErrorMessage(QString("STATUS: Welcome to nnUNet. " + QString::number(m_GpuLoader.GetGPUCount()) + " GPUs were detected.")); } mainLayout->addLayout(m_Controls.verticalLayout); Superclass::InitializeUI(mainLayout); m_UI_ROWS = m_Controls.advancedSettingsLayout->rowCount(); // Must do. Row count is correct only here. - - if (nullptr != m_Controls.modeldirectoryBox) - { - QString setVal = m_Settings.value("nnUNet/LastRESULTS_FOLDERPath").toString(); - m_Controls.modeldirectoryBox->setDirectory(setVal); - } + DisableEverything(); + QString lastSelectedPyEnv = m_Settings.value("nnUNet/LastPythonPath").toString(); + m_Controls.pythonEnvComboBox->setCurrentText(lastSelectedPyEnv); } void QmitknnUNetToolGUI::OnPreviewRequested() { mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); if (nullptr != tool) { + QString pythonPathTextItem = ""; try { size_t hashKey(0); m_Controls.previewButton->setEnabled(false); // To prevent misclicked back2back prediction. qApp->processEvents(); tool->PredictOn(); // purposefully placed to make tool->GetMTime different than before. QString modelName = m_Controls.modelBox->currentText(); if (modelName.startsWith("ensemble", Qt::CaseInsensitive)) { ProcessEnsembleModelsParams(tool); } else { ProcessModelParams(tool); } - QString pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText(); - QString pythonPath = pythonPathTextItem.mid(pythonPathTextItem.indexOf(" ") + 1); - bool isNoPip = m_Controls.nopipBox->isChecked(); -#ifdef _WIN32 - if (!isNoPip && !(pythonPath.endsWith("Scripts", Qt::CaseInsensitive) || - pythonPath.endsWith("Scripts/", Qt::CaseInsensitive))) - { - pythonPath += QDir::separator() + QString("Scripts"); - } -#else - if (!(pythonPath.endsWith("bin", Qt::CaseInsensitive) || pythonPath.endsWith("bin/", Qt::CaseInsensitive))) - { - pythonPath += QDir::separator() + QString("bin"); - } -#endif - std::string nnUNetDirectory; - if (isNoPip) - { - nnUNetDirectory = m_Controls.codedirectoryBox->directory().toStdString(); - } - else if (!IsNNUNetInstalled(pythonPath)) + pythonPathTextItem = m_Controls.pythonEnvComboBox->currentText(); + QString pythonPath = m_PythonPath; + if (!IsNNUNetInstalled(pythonPath)) { throw std::runtime_error("nnUNet is not detected in the selected python environment. Please select a valid " "python environment or install nnUNet."); } - - tool->SetnnUNetDirectory(nnUNetDirectory); tool->SetPythonPath(pythonPath.toStdString()); tool->SetModelDirectory(m_ParentFolder->getResultsFolder().toStdString()); // checkboxes tool->SetMirror(m_Controls.mirrorBox->isChecked()); tool->SetMixedPrecision(m_Controls.mixedPrecisionBox->isChecked()); - tool->SetNoPip(isNoPip); + tool->SetNoPip(false); bool doCache = m_Controls.enableCachingCheckBox->isChecked(); // Spinboxes tool->SetGpuId(FetchSelectedGPUFromUI()); // Multi-Modal tool->MultiModalOff(); if (m_Controls.multiModalBox->isChecked()) { if (m_Controls.multiModalSpinBox->value() > 0) { tool->m_OtherModalPaths.clear(); tool->m_OtherModalPaths = FetchMultiModalImagesFromUI(); tool->MultiModalOn(); } else { throw std::runtime_error("Please select more than one modalities for a multi-modal task. If you " "would like to use only one modality then uncheck the Multi-Modal option."); } } if (doCache) { hashKey = nnUNetCache::GetUniqueHash(tool->m_ParamQ); if (m_Cache.contains(hashKey)) { tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before. } } if (tool->GetPredict()) { tool->m_InputBuffer = nullptr; WriteStatusMessage(QString("STATUS: Starting Segmentation task... This might take a while.")); tool->UpdatePreview(); if (nullptr == tool->GetOutputBuffer()) { SegmentationProcessFailed(); } else { SegmentationResultHandler(tool); if (doCache) { AddToCache(hashKey, tool->GetMLPreview()); } } tool->PredictOff(); // purposefully placed to make tool->GetMTime different than before. } else { MITK_INFO << "won't do segmentation. Key found: " << QString::number(hashKey).toStdString(); if (m_Cache.contains(hashKey)) { nnUNetCache *cacheObject = m_Cache[hashKey]; MITK_INFO << "fetched pointer " << cacheObject->m_SegCache.GetPointer(); - tool->SetNodeProperties(const_cast(cacheObject->m_SegCache.GetPointer())); - SegmentationResultHandler(tool); + tool->SetOutputBuffer(const_cast(cacheObject->m_SegCache.GetPointer())); + SegmentationResultHandler(tool, true); } } m_Controls.previewButton->setEnabled(true); } catch (const std::exception &e) { std::stringstream errorMsg; errorMsg << "STATUS: Error while processing parameters for nnUNet segmentation. Reason: " << e.what(); ShowErrorMessage(errorMsg.str()); WriteErrorMessage(QString::fromStdString(errorMsg.str())); m_Controls.previewButton->setEnabled(true); tool->PredictOff(); return; } catch (...) { std::string errorMsg = "Unkown error occured while generation nnUNet segmentation."; ShowErrorMessage(errorMsg); m_Controls.previewButton->setEnabled(true); tool->PredictOff(); return; } + if (!pythonPathTextItem.isEmpty()) + { // only cache if the prediction ended without errors. + m_Settings.setValue("nnUNet/LastPythonPath", pythonPathTextItem); + } } } std::vector QmitknnUNetToolGUI::FetchMultiModalImagesFromUI() { std::vector modals; if (m_Controls.multiModalBox->isChecked() && !m_Modalities.empty()) { std::set nodeNames; // set container for keeping names of all nodes to check if they are added twice. for (QmitkDataStorageComboBox *modality : m_Modalities) { if (nodeNames.find(modality->GetSelectedNode()->GetName()) == nodeNames.end()) { modals.push_back(dynamic_cast(modality->GetSelectedNode()->GetData())); nodeNames.insert(modality->GetSelectedNode()->GetName()); } else { throw std::runtime_error("Same modality is selected more than once. Please change your selection."); break; } } } return modals; } bool QmitknnUNetToolGUI::IsNNUNetInstalled(const QString &pythonPath) { QString fullPath = pythonPath; #ifdef _WIN32 if (!(fullPath.endsWith("Scripts", Qt::CaseInsensitive) || fullPath.endsWith("Scripts/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("Scripts"); } #else if (!(fullPath.endsWith("bin", Qt::CaseInsensitive) || fullPath.endsWith("bin/", Qt::CaseInsensitive))) { fullPath += QDir::separator() + QString("bin"); } #endif fullPath = fullPath.mid(fullPath.indexOf(" ") + 1); - return QFile::exists(fullPath + QDir::separator() + QString("nnUNet_predict")); + bool isExists = QFile::exists(fullPath + QDir::separator() + QString("nnUNet_predict")) && + QFile::exists(fullPath + QDir::separator() + QString("python3")); + return isExists; } void QmitknnUNetToolGUI::ShowErrorMessage(const std::string &message, QMessageBox::Icon icon) { this->setCursor(Qt::ArrowCursor); QMessageBox *messageBox = new QMessageBox(icon, nullptr, message.c_str()); messageBox->exec(); delete messageBox; MITK_WARN << message; } void QmitknnUNetToolGUI::WriteStatusMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: white"); } void QmitknnUNetToolGUI::WriteErrorMessage(const QString &message) { m_Controls.statusLabel->setText(message); m_Controls.statusLabel->setStyleSheet("font-weight: bold; color: red"); } void QmitknnUNetToolGUI::ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer tool) { if (m_EnsembleParams[0]->modelBox->currentText() == m_EnsembleParams[1]->modelBox->currentText()) { throw std::runtime_error("Both models you have selected for ensembling are the same."); } QString taskName = m_Controls.taskBox->currentText(); bool isPPJson = m_Controls.postProcessingCheckBox->isChecked(); std::vector requestQ; QString ppDirFolderNamePart1 = "ensemble_"; QStringList ppDirFolderNameParts; for (auto &layout : m_EnsembleParams) { QStringList ppDirFolderName; QString modelName = layout->modelBox->currentText(); ppDirFolderName << modelName; ppDirFolderName << "__"; QString trainer = layout->trainerBox->currentText(); ppDirFolderName << trainer; ppDirFolderName << "__"; QString planId = layout->plannerBox->currentText(); ppDirFolderName << planId; if (!IsModelExists(modelName, taskName, QString(trainer + "__" + planId))) { std::string errorMsg = "The configuration " + modelName.toStdString() + " you have selected doesn't exist. Check your Results Folder again."; throw std::runtime_error(errorMsg); } std::vector testfold = FetchSelectedFoldsFromUI(layout->foldBox); mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, testfold); requestQ.push_back(modelObject); ppDirFolderNameParts << ppDirFolderName.join(QString("")); } tool->EnsembleOn(); if (isPPJson) { QString ppJsonFilePossibility1 = QDir::cleanPath( m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" + QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.first() + "--" + ppDirFolderNameParts.last() + QDir::separator() + "postprocessing.json"); QString ppJsonFilePossibility2 = QDir::cleanPath( m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + "ensembles" + QDir::separator() + taskName + QDir::separator() + ppDirFolderNamePart1 + ppDirFolderNameParts.last() + "--" + ppDirFolderNameParts.first() + QDir::separator() + "postprocessing.json"); if (QFile(ppJsonFilePossibility1).exists()) { tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility1.toStdString()); const QString statusMsg = "Post Processing JSON file found: " + ppJsonFilePossibility1; WriteStatusMessage(statusMsg); } else if (QFile(ppJsonFilePossibility2).exists()) { tool->SetPostProcessingJsonDirectory(ppJsonFilePossibility2.toStdString()); const QString statusMsg = "Post Processing JSON file found:" + ppJsonFilePossibility2; WriteStatusMessage(statusMsg); } else { std::string errorMsg = "No post processing file was found for the selected ensemble combination. Continuing anyway..."; ShowErrorMessage(errorMsg); } } tool->m_ParamQ.clear(); tool->m_ParamQ = requestQ; } void QmitknnUNetToolGUI::ProcessModelParams(mitk::nnUNetTool::Pointer tool) { tool->EnsembleOff(); std::vector requestQ; QString modelName = m_Controls.modelBox->currentText(); QString taskName = m_Controls.taskBox->currentText(); QString trainer = m_Controls.trainerBox->currentText(); QString planId = m_Controls.plannerBox->currentText(); std::vector fetchedFolds = FetchSelectedFoldsFromUI(m_Controls.foldBox); mitk::ModelParams modelObject = MapToRequest(modelName, taskName, trainer, planId, fetchedFolds); requestQ.push_back(modelObject); tool->m_ParamQ.clear(); tool->m_ParamQ = requestQ; } bool QmitknnUNetToolGUI::IsModelExists(const QString &modelName, const QString &taskName, const QString &trainerPlanner) { QString modelSearchPath = QDir::cleanPath(m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + modelName + QDir::separator() + taskName + QDir::separator() + trainerPlanner); if (QDir(modelSearchPath).exists()) { return true; } return false; } void QmitknnUNetToolGUI::CheckAllInCheckableComboBox(ctkCheckableComboBox *foldBox) { // Recalling all added items to check-mark it. const QAbstractItemModel *qaim = foldBox->checkableModel(); auto rows = qaim->rowCount(); for (std::remove_const_t i = 0; i < rows; ++i) { const QModelIndex mi = qaim->index(i, 0); foldBox->setCheckState(mi, Qt::Checked); } } std::pair QmitknnUNetToolGUI::ExtractTrainerPlannerFromString(QStringList trainerPlanners) { QString splitterString = "__"; QStringList trainers, planners; for (const auto &trainerPlanner : trainerPlanners) { trainers << trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts).first(); planners << trainerPlanner.split(splitterString, QString::SplitBehavior::SkipEmptyParts).last(); } trainers.removeDuplicates(); planners.removeDuplicates(); return std::make_pair(trainers, planners); } std::vector QmitknnUNetToolGUI::FetchSelectedFoldsFromUI(ctkCheckableComboBox *foldBox) { std::vector folds; - if (!(foldBox->allChecked() || foldBox->noneChecked())) + if (foldBox->noneChecked()) { - QModelIndexList foldList = foldBox->checkedIndexes(); - for (const auto &index : foldList) - { - QString foldQString = foldBox->itemText(index.row()).split("_", QString::SplitBehavior::SkipEmptyParts).last(); - folds.push_back(foldQString.toStdString()); - } + CheckAllInCheckableComboBox(foldBox); + } + QModelIndexList foldList = foldBox->checkedIndexes(); + for (const auto &index : foldList) + { + QString foldQString = foldBox->itemText(index.row()).split("_", QString::SplitBehavior::SkipEmptyParts).last(); + folds.push_back(foldQString.toStdString()); } return folds; } void QmitknnUNetToolGUI::OnClearCachePressed() { m_Cache.clear(); UpdateCacheCountOnUI(); } void QmitknnUNetToolGUI::UpdateCacheCountOnUI() { QString cacheText = m_CACHE_COUNT_BASE_LABEL + QString::number(m_Cache.size()); m_Controls.cacheCountLabel->setText(cacheText); } void QmitknnUNetToolGUI::AddToCache(size_t &hashKey, mitk::LabelSetImage::ConstPointer mlPreview) { nnUNetCache *newCacheObj = new nnUNetCache; newCacheObj->m_SegCache = mlPreview; m_Cache.insert(hashKey, newCacheObj); MITK_INFO << "New hash: " << hashKey << " " << newCacheObj->m_SegCache.GetPointer(); UpdateCacheCountOnUI(); } void QmitknnUNetToolGUI::SetGPUInfo() { std::vector specs = m_GpuLoader.GetAllGPUSpecs(); for (const QmitkGPUSpec &gpuSpec : specs) { m_Controls.gpuComboBox->addItem(QString::number(gpuSpec.id) + ": " + gpuSpec.name + " (" + gpuSpec.memory + ")"); } if (specs.empty()) { m_Controls.gpuComboBox->setEditable(true); m_Controls.gpuComboBox->addItem(QString::number(0)); m_Controls.gpuComboBox->setValidator(new QIntValidator(0, 999, this)); } } unsigned int QmitknnUNetToolGUI::FetchSelectedGPUFromUI() { QString gpuInfo = m_Controls.gpuComboBox->currentText(); if (m_GpuLoader.GetGPUCount() == 0) { return static_cast(gpuInfo.toInt()); } else { QString gpuId = gpuInfo.split(":", QString::SplitBehavior::SkipEmptyParts).first(); return static_cast(gpuId.toInt()); } } + +QString QmitknnUNetToolGUI::FetchResultsFolderFromEnv() +{ + const char *pathVal = itksys::SystemTools::GetEnv("RESULTS_FOLDER"); + QString retVal; + if (pathVal) + { + retVal = QString::fromUtf8(pathVal); + } + else + { + retVal = m_Settings.value("nnUNet/LastRESULTS_FOLDERPath").toString(); + } + return retVal; +} + +QString QmitknnUNetToolGUI::DumpJSONfromPickle(const QString &parentPath) +{ + const QString picklePath = parentPath + QDir::separator() + QString("plans.pkl"); + const QString jsonPath = parentPath + QDir::separator() + QString("mitk_export.json"); + if (!QFile::exists(jsonPath)) + { + mitk::ProcessExecutor::Pointer spExec = mitk::ProcessExecutor::New(); + itk::CStyleCommand::Pointer spCommand = itk::CStyleCommand::New(); + mitk::ProcessExecutor::ArgumentListType args; + args.push_back("-c"); + std::string pythonCode; // python syntax to parse plans.pkl file and export as Json file. + pythonCode.append("import pickle;"); + pythonCode.append("import json;"); + pythonCode.append("loaded_pickle = pickle.load(open('"); + pythonCode.append(picklePath.toStdString()); + pythonCode.append("','rb'));"); + pythonCode.append("modal_dict = {key: loaded_pickle[key] for key in loaded_pickle.keys() if key in " + "['modalities','num_modalities']};"); + pythonCode.append("json.dump(modal_dict, open('"); + pythonCode.append(jsonPath.toStdString()); + pythonCode.append("', 'w'))"); + + args.push_back(pythonCode); + try + { + spExec->Execute(m_PythonPath.toStdString(), "python3", args); + } + catch (const mitk::Exception &e) + { + MITK_ERROR << "Pickle parsing FAILED!" << e.GetDescription(); // SHOW ERROR + WriteStatusMessage( + "Parsing failed in backend. Multiple Modalities will now have to be manually entered by the user."); + return QString(""); + } + } + return jsonPath; +} + +void QmitknnUNetToolGUI::DisplayMultiModalInfoFromJSON(const QString &jsonPath) +{ + if (QFile::exists(jsonPath)) + { + QFile file(jsonPath); + if (file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + QByteArray bytes = file.readAll(); + file.close(); + + QJsonParseError jsonError; + QJsonDocument document = QJsonDocument::fromJson(bytes, &jsonError); + if (jsonError.error != QJsonParseError::NoError) + { + MITK_INFO << "fromJson failed: " << jsonError.errorString().toStdString() << endl; + return; + } + if (document.isObject()) + { + QJsonObject jsonObj = document.object(); + int num_mods = jsonObj["num_modalities"].toInt(); + ClearAllModalLabels(); + if (num_mods > 1) + { + m_Controls.multiModalBox->setChecked(true); + m_Controls.multiModalBox->setEnabled(false); + m_Controls.multiModalSpinBox->setValue(num_mods - 1); + m_Controls.advancedSettingsLayout->update(); + QJsonObject obj = jsonObj.value("modalities").toObject(); + QStringList keys = obj.keys(); + int count = 0; + for (auto key : keys) + { + auto value = obj.take(key); + QLabel *label = new QLabel("" + value.toString() + "", this); + m_ModalLabels.push_back(label); + m_Controls.advancedSettingsLayout->addWidget(label, m_UI_ROWS + 1 + count, 0); + count++; + } + m_Controls.multiModalSpinBox->setMinimum(num_mods - 1); + m_Controls.advancedSettingsLayout->update(); + } + else + { + m_Controls.multiModalSpinBox->setMinimum(0); + m_Controls.multiModalBox->setChecked(false); + } + } + } + } +} diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h index b5a6106778..601f36453e 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUI.h @@ -1,298 +1,342 @@ /*============================================================================ 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.s ============================================================================*/ #ifndef QmitknnUNetToolGUI_h_Included #define QmitknnUNetToolGUI_h_Included #include "QmitkAutoMLSegmentationToolGUIBase.h" #include "QmitknnUNetFolderParser.h" #include "QmitknnUNetGPU.h" #include "mitknnUnetTool.h" #include "ui_QmitknnUNetToolGUIControls.h" #include #include #include #include #include #include #include class nnUNetCache { public: mitk::LabelSetImage::ConstPointer m_SegCache; static size_t GetUniqueHash(std::vector &requestQ) { size_t hashCode = 0; for (mitk::ModelParams &request : requestQ) { boost::hash_combine(hashCode, request.generateHash()); } return hashCode; } }; class MITKSEGMENTATIONUI_EXPORT QmitknnUNetToolGUI : public QmitkAutoMLSegmentationToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitknnUNetToolGUI, QmitkAutoMLSegmentationToolGUIBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); QCache m_Cache; protected slots: /** * @brief Qt slot * */ void OnPreviewRequested(); /** * @brief Qt slot * */ void OnDirectoryChanged(const QString &); /** * @brief Qt slot * */ void OnModelChanged(const QString &); /** * @brief Qt slot * */ void OnTaskChanged(const QString &); /** * @brief Qt slot * */ void OnTrainerChanged(const QString &); /** * @brief Qt slot * */ void OnCheckBoxChanged(int); /** * @brief Qthread slot to capture failures from thread worker and * shows error message * */ void SegmentationProcessFailed(); /** * @brief Qthread to capture sucessfull nnUNet segmentation. * Further, renders the LabelSet image */ - void SegmentationResultHandler(mitk::nnUNetTool *); + void SegmentationResultHandler(mitk::nnUNetTool *, bool forceRender = false); /** * @brief Qt Slot * */ void OnModalitiesNumberChanged(int); /** * @brief Qt Slot * */ void OnPythonPathChanged(const QString &); /** * @brief Qt slot * */ void OnRefreshPresssed(); /** * @brief Qt slot * */ void OnClearCachePressed(); protected: QmitknnUNetToolGUI(); ~QmitknnUNetToolGUI() = default; void ConnectNewTool(mitk::AutoSegmentationWithPreviewTool *newTool) override; void InitializeUI(QBoxLayout *mainLayout) override; void EnableWidgets(bool enabled) override; private: + + /** + * @brief Clears all displayed modal labels and widgets from GUI. + * + */ + void ClearAllModalities(); + + /** + * @brief Parses Json file containing modality info and populates + * labels and selection widgets accordingly on the GUI. + */ + void DisplayMultiModalInfoFromJSON(const QString&); + + /** + * @brief Clears all modality labels previously populated from GUI + * + */ + + void ClearAllModalLabels(); + + /** + * @brief Runs a set of python commands to read "plans.pkl" and extract + * modality information required for inferencing. This information is exported + * as json file : "mitk_export.json". + * + * @return QString + */ + QString DumpJSONfromPickle(const QString&); + + /** + * @brief Searches RESULTS_FOLDER environment variable. If not found, + * returns from the QSettings stored last used path value. + * @return QString + */ + QString FetchResultsFolderFromEnv(); /** * @brief Returns GPU id of the selected GPU from the Combo box. * * @return unsigned int */ unsigned int FetchSelectedGPUFromUI(); /** * @brief Adds GPU information to the gpu combo box. * In case, there aren't any GPUs avaialble, the combo box will be * rendered editable. */ void SetGPUInfo(); /** * @brief Inserts the hash and segmentation into cache and * updates count on UI. */ void AddToCache(size_t&, mitk::LabelSetImage::ConstPointer); /** * @brief Checks all the entries of the ctkCheckableComboBox ui widget. * This feature is not present in ctkCheckableComboBox API. */ void CheckAllInCheckableComboBox(ctkCheckableComboBox *); /** * @brief Parses the folder names containing trainer and planner together and, * returns it as separate lists. * @return std::pair */ std::pair ExtractTrainerPlannerFromString(QStringList); /** * @brief Parses the ensemble UI elements and sets to nnUNetTool object pointer. * */ void ProcessEnsembleModelsParams(mitk::nnUNetTool::Pointer); /** * @brief Parses the UI elements and sets to nnUNetTool object pointer. * */ void ProcessModelParams(mitk::nnUNetTool::Pointer); /** * @brief Creates and renders QmitknnUNetTaskParamsUITemplate layout for ensemble input. */ void ShowEnsembleLayout(bool visible = true); /** * @brief Creates a QMessage object and shows on screen. */ void ShowErrorMessage(const std::string &, QMessageBox::Icon = QMessageBox::Critical); /** * @brief Writes any message in white on the tool pane. */ void WriteStatusMessage(const QString &); /** * @brief Writes any message in red on the tool pane. */ void WriteErrorMessage(const QString &); /** * @brief Searches and parses paths of python virtual enviroments * from predefined lookout locations */ void AutoParsePythonPaths(); /** * @brief Check if pretrained model sub folder inside RESULTS FOLDER exist. */ bool IsModelExists(const QString &, const QString &, const QString &); /** * @brief Clears all combo boxes * Any new combo box added in the future can be featured here for clearance. * */ void ClearAllComboBoxes(); + /** + * @brief Disable/deactivates the nnUNet GUI. + * Clears any multi modal labels and selection widgets, as well. + */ + void DisableEverything(); + /** * @brief Checks if nnUNet_predict command is valid in the selected python virtual environment. * * @return bool */ bool IsNNUNetInstalled(const QString &); /** * @brief Mapper function to map QString entries from UI to ModelParam attributes. * * @return mitk::ModelParams */ mitk::ModelParams MapToRequest( const QString &, const QString &, const QString &, const QString &, const std::vector &); /** * @brief Returns checked fold names from the ctk-Checkable-ComboBox. * * @return std::vector */ std::vector FetchSelectedFoldsFromUI(ctkCheckableComboBox *); /** * @brief Returns all paths from the dynamically generated ctk-path-line-edit boxes. * * @return std::vector */ std::vector FetchMultiModalImagesFromUI(); /** * @brief Updates cache count on UI. * */ void UpdateCacheCountOnUI(); Ui_QmitknnUNetToolGUIControls m_Controls; QmitkGPULoader m_GpuLoader; /** * @brief Stores all dynamically added ctk-path-line-edit UI elements. * */ std::vector m_Modalities; + std::vector m_ModalLabels; std::vector> m_EnsembleParams; mitk::NodePredicateBase::Pointer m_MultiModalPredicate; + QString m_PythonPath; + /** * @brief Stores row count of the "advancedSettingsLayout" layout element. This value helps dynamically add * ctk-path-line-edit UI elements at the right place. Forced to initialize in the InitializeUI method since there is * no guarantee of retrieving exact row count anywhere else. * */ int m_UI_ROWS; /** * @brief Stores path of the model director (RESULTS_FOLDER appended by "nnUNet"). * */ std::shared_ptr m_ParentFolder = nullptr; /** * @brief Valid list of models supported by nnUNet * */ const QStringList m_VALID_MODELS = {"2d", "3d_lowres", "3d_fullres", "3d_cascade_fullres", "ensembles"}; const QString m_CACHE_COUNT_BASE_LABEL = "Cached Items: "; /** * @brief For storing values across sessions. Currently, RESULTS_FOLDER value is cached using this. */ QSettings m_Settings; }; #endif diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUIControls.ui index 82e12d2b63..a3d0a1ce7f 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUIControls.ui +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolGUIControls.ui @@ -1,478 +1,439 @@ QmitknnUNetToolGUIControls 0 0 877 711 0 0 100 0 100000 100000 QmitknnUNetToolWidget 0 0 0 0 - + - + - + Refresh Results Folder - + 0 0 Python Path: - + 0 0 Plan: - + 0 0 Trainer: - + 0 0 nnUNet Results Folder: - + - - - - Stop - - - 0 0 Multi-Modal: 0 0 - <html><head/><body><p>Welcome to nnUNet in MITK. [Experimental]</p><p>Please note that this is only an interface to nnUNet. MITK does not ship with nnUNet. Make sure to have a working Python environment with nnUNet set up beforehand. Choose that environment in the Python Path before inferencing.</p><p>Refer to <a href="https://github.com/MIC-DKFZ/nnUNet"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/MIC-DKFZ/nnUNet</span></a> to learn everything about the nnUNet.</p></body></html> + <html><head/><body><p>Welcome to nnUNet in MITK. [Experimental]</p><p>Please note that this is only an interface to nnUNet. MITK does not ship with nnUNet. Make sure to have a working Python environment with nnUNet set up beforehand. Choose that environment in the Python Path before inferencing. </p><p>Refer to <a href="https://github.com/MIC-DKFZ/nnUNet"><span style=" text-decoration: underline; color:#0000ff;">https://github.com/MIC-DKFZ/nnUNet</span></a> to learn everything about the nnUNet.</p><p>Also, note that if multiple Preview objects are selected to Confirm, they will be merged. </p><p><br/></p></body></html> Qt::RichText true - + - + Configuration: - + - + 0 0 Task: - + 0 0 No. of Extra Modalities: - + Fold: - + 0 0 Advanced Qt::AlignRight true 5 true 0 0 0 0 6 - - - - - 0 - 0 - - - - No Pip: - - - - - - 0 0 Mixed Precision: true 0 0 GPU Id: 0 0 Enable Mirroring: true - - - - - 0 - 0 - - - - nnUNet Path: - - - - - - - + 0 0 Use Postprocessing JSON: - + true 0 0 Enable Caching: true Clear Cache 0 0 Cached Items: 0 0 0 100000 16777215 Preview 0 0 true ctkDirectoryButton QWidget
ctkDirectoryButton.h
1
ctkComboBox QComboBox
ctkComboBox.h
1
ctkCheckableComboBox QComboBox
ctkCheckableComboBox.h
1
ctkCheckBox QCheckBox
ctkCheckBox.h
1
ctkCollapsibleGroupBox QGroupBox
ctkCollapsibleGroupBox.h
1
diff --git a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp index 39a386248c..4b82f39ed2 100644 --- a/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitknnUNetToolSlots.cpp @@ -1,409 +1,478 @@ #include "QmitknnUNetToolGUI.h" #include #include #include #include #include void QmitknnUNetToolGUI::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); } +void QmitknnUNetToolGUI::ClearAllModalities() +{ + m_Controls.multiModalSpinBox->setMinimum(0); + m_Controls.multiModalBox->setChecked(false); + ClearAllModalLabels(); +} + +void QmitknnUNetToolGUI::ClearAllModalLabels() +{ + for (auto modalLabel : m_ModalLabels) + { + delete modalLabel; // delete the layout item + m_ModalLabels.pop_back(); + } + m_Controls.advancedSettingsLayout->update(); +} + +void QmitknnUNetToolGUI::DisableEverything() +{ + m_Controls.modeldirectoryBox->setEnabled(false); + m_Controls.refreshdirectoryBox->setEnabled(false); + m_Controls.previewButton->setEnabled(false); + m_Controls.multiModalSpinBox->setVisible(false); + m_Controls.multiModalBox->setEnabled(false); + ClearAllComboBoxes(); + ClearAllModalities(); +} + void QmitknnUNetToolGUI::ClearAllComboBoxes() { m_Controls.modelBox->clear(); m_Controls.taskBox->clear(); m_Controls.foldBox->clear(); m_Controls.trainerBox->clear(); + m_Controls.plannerBox->clear(); for (auto &layout : m_EnsembleParams) { layout->modelBox->clear(); layout->trainerBox->clear(); layout->plannerBox->clear(); layout->foldBox->clear(); } } void QmitknnUNetToolGUI::OnRefreshPresssed() { const QString resultsFolder = m_Controls.modeldirectoryBox->directory(); OnDirectoryChanged(resultsFolder); } void QmitknnUNetToolGUI::OnDirectoryChanged(const QString &resultsFolder) { m_Controls.previewButton->setEnabled(false); - this->ClearAllComboBoxes(); + ClearAllComboBoxes(); + ClearAllModalities(); m_ParentFolder = std::make_shared(resultsFolder); - auto models = m_ParentFolder->getModelNames(); - std::for_each(models.begin(), - models.end(), - [this](QString model) - { - if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive)) - m_Controls.modelBox->addItem(model); - }); - m_Settings.setValue("nnUNet/LastRESULTS_FOLDERPath",resultsFolder); + auto tasks = m_ParentFolder->getAllTasks(); + tasks.removeDuplicates(); + std::for_each(tasks.begin(), tasks.end(), [this](QString task) { m_Controls.taskBox->addItem(task); }); + m_Settings.setValue("nnUNet/LastRESULTS_FOLDERPath", resultsFolder); } void QmitknnUNetToolGUI::OnModelChanged(const QString &model) { if (model.isEmpty()) { return; } + ClearAllModalities(); + auto selectedTask = m_Controls.taskBox->currentText(); ctkComboBox *box = qobject_cast(sender()); if (box == m_Controls.modelBox) { - m_Controls.taskBox->clear(); - auto tasks = m_ParentFolder->getTasksForModel(model); - std::for_each(tasks.begin(), tasks.end(), [this](QString task) { m_Controls.taskBox->addItem(task); }); + if (model == m_VALID_MODELS.last()) + { + m_Controls.trainerBox->setVisible(false); + m_Controls.trainerLabel->setVisible(false); + m_Controls.plannerBox->setVisible(false); + m_Controls.plannerLabel->setVisible(false); + m_Controls.foldBox->setVisible(false); + m_Controls.foldLabel->setVisible(false); + ShowEnsembleLayout(true); + auto models = m_ParentFolder->getModelsForTask(m_Controls.taskBox->currentText()); + models.removeDuplicates(); + models.removeOne(m_VALID_MODELS.last()); + for (auto &layout : m_EnsembleParams) + { + layout->modelBox->clear(); + layout->trainerBox->clear(); + layout->plannerBox->clear(); + std::for_each(models.begin(), + models.end(), + [&layout, this](QString model) + { + if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive)) + layout->modelBox->addItem(model); + }); + } + m_Controls.previewButton->setEnabled(true); + } + else + { + m_Controls.trainerBox->setVisible(true); + m_Controls.trainerLabel->setVisible(true); + m_Controls.plannerBox->setVisible(true); + m_Controls.plannerLabel->setVisible(true); + m_Controls.foldBox->setVisible(true); + m_Controls.foldLabel->setVisible(true); + m_Controls.previewButton->setEnabled(false); + ShowEnsembleLayout(false); + auto trainerPlanners = m_ParentFolder->getTrainerPlannersForTask(selectedTask, model); + QStringList trainers, planners; + std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners); + m_Controls.trainerBox->clear(); + m_Controls.plannerBox->clear(); + std::for_each( + trainers.begin(), trainers.end(), [this](QString trainer) { m_Controls.trainerBox->addItem(trainer); }); + std::for_each( + planners.begin(), planners.end(), [this](QString planner) { m_Controls.plannerBox->addItem(planner); }); + } } else if (!m_EnsembleParams.empty()) { for (auto &layout : m_EnsembleParams) { if (box == layout->modelBox) { layout->trainerBox->clear(); layout->plannerBox->clear(); - auto trainerPlanners = - m_ParentFolder->getTrainerPlannersForTask(m_Controls.taskBox->currentText(), model); + auto trainerPlanners = m_ParentFolder->getTrainerPlannersForTask(selectedTask, model); QStringList trainers, planners; std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners); - - std::for_each( - trainers.begin(), trainers.end(), [&layout](const QString& trainer) { layout->trainerBox->addItem(trainer); }); - std::for_each( - planners.begin(), planners.end(), [&layout](const QString& planner) { layout->plannerBox->addItem(planner); }); + std::for_each(trainers.begin(), + trainers.end(), + [&layout](const QString &trainer) { layout->trainerBox->addItem(trainer); }); + std::for_each(planners.begin(), + planners.end(), + [&layout](const QString &planner) { layout->plannerBox->addItem(planner); }); break; } } } } void QmitknnUNetToolGUI::OnTaskChanged(const QString &task) { if (task.isEmpty()) { return; } - m_Controls.trainerBox->clear(); - m_Controls.plannerBox->clear(); - if (m_Controls.modelBox->currentText() == m_VALID_MODELS.last()) - { - m_Controls.trainerBox->setVisible(false); - m_Controls.trainerLabel->setVisible(false); - m_Controls.plannerBox->setVisible(false); - m_Controls.plannerLabel->setVisible(false); - m_Controls.foldBox->setVisible(false); - m_Controls.foldLabel->setVisible(false); - ShowEnsembleLayout(true); - auto models = m_ParentFolder->getModelsForTask(m_Controls.taskBox->currentText()); - models.removeDuplicates(); - models.removeOne(m_VALID_MODELS.last()); - - for (auto &layout : m_EnsembleParams) - { - layout->modelBox->clear(); - layout->trainerBox->clear(); - layout->plannerBox->clear(); - std::for_each(models.begin(), - models.end(), - [&layout, this](QString model) - { - if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive)) - layout->modelBox->addItem(model); - }); - } - m_Controls.previewButton->setEnabled(true); - } - else + m_Controls.modelBox->clear(); + auto models = m_ParentFolder->getModelsForTask(task); + models.removeDuplicates(); + if (!models.contains(m_VALID_MODELS.last(), Qt::CaseInsensitive)) { - m_Controls.trainerBox->setVisible(true); - m_Controls.trainerLabel->setVisible(true); - m_Controls.plannerBox->setVisible(true); - m_Controls.plannerLabel->setVisible(true); - m_Controls.foldBox->setVisible(true); - m_Controls.foldLabel->setVisible(true); - m_Controls.previewButton->setEnabled(false); - ShowEnsembleLayout(false); - auto trainerPlanners = - m_ParentFolder->getTrainerPlannersForTask(task, m_Controls.modelBox->currentText()); - QStringList trainers, planners; - std::tie(trainers, planners) = ExtractTrainerPlannerFromString(trainerPlanners); - std::for_each( - trainers.begin(), trainers.end(), [this](QString trainer) { m_Controls.trainerBox->addItem(trainer); }); - std::for_each( - planners.begin(), planners.end(), [this](QString planner) { m_Controls.plannerBox->addItem(planner); }); + models << m_VALID_MODELS.last(); // add ensemble even if folder doesn't exist } + std::for_each(models.begin(), + models.end(), + [this](QString model) + { + if (m_VALID_MODELS.contains(model, Qt::CaseInsensitive)) + m_Controls.modelBox->addItem(model); + }); } void QmitknnUNetToolGUI::OnTrainerChanged(const QString &plannerSelected) { if (plannerSelected.isEmpty()) { return; } auto *box = qobject_cast(sender()); if (box == m_Controls.plannerBox) { m_Controls.foldBox->clear(); auto selectedTrainer = m_Controls.trainerBox->currentText(); auto selectedTask = m_Controls.taskBox->currentText(); auto selectedModel = m_Controls.modelBox->currentText(); auto folds = m_ParentFolder->getFoldsForTrainerPlanner( selectedTrainer, plannerSelected, selectedTask, selectedModel); std::for_each(folds.begin(), folds.end(), [this](QString fold) { if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet m_Controls.foldBox->addItem(fold); }); if (m_Controls.foldBox->count() != 0) { CheckAllInCheckableComboBox(m_Controls.foldBox); m_Controls.previewButton->setEnabled(true); + const QString parentPath = QDir::cleanPath(m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + + QDir::separator() + selectedModel + QDir::separator() + selectedTask + + QDir::separator() + selectedTrainer + QString("__") + plannerSelected); + const QString jsonPath = this->DumpJSONfromPickle(parentPath); + if (!jsonPath.isEmpty()) + { + this->DisplayMultiModalInfoFromJSON(jsonPath); + } } } else if (!m_EnsembleParams.empty()) { for (auto &layout : m_EnsembleParams) { if (box == layout->plannerBox) { layout->foldBox->clear(); auto selectedTrainer = layout->trainerBox->currentText(); auto selectedTask = m_Controls.taskBox->currentText(); auto selectedModel = layout->modelBox->currentText(); auto folds = m_ParentFolder->getFoldsForTrainerPlanner( selectedTrainer, plannerSelected, selectedTask, selectedModel); std::for_each(folds.begin(), folds.end(), - [&layout](const QString& fold) + [&layout](const QString &fold) { if (fold.startsWith("fold_", Qt::CaseInsensitive)) // imposed by nnUNet layout->foldBox->addItem(fold); }); if (layout->foldBox->count() != 0) { CheckAllInCheckableComboBox(layout->foldBox); m_Controls.previewButton->setEnabled(true); + const QString parentPath = QDir::cleanPath( + m_ParentFolder->getResultsFolder() + QDir::separator() + "nnUNet" + QDir::separator() + selectedModel + + QDir::separator() + selectedTask + QDir::separator() + selectedTrainer + QString("__") + plannerSelected); + const QString jsonPath = this->DumpJSONfromPickle(parentPath); + if (!jsonPath.isEmpty()) + { + this->DisplayMultiModalInfoFromJSON(jsonPath); + } } break; } } } } void QmitknnUNetToolGUI::OnPythonPathChanged(const QString &pyEnv) { if (pyEnv == QString("Select")) { QString path = QFileDialog::getExistingDirectory(m_Controls.pythonEnvComboBox->parentWidget(), "Python Path", "dir"); if (!path.isEmpty()) { + OnPythonPathChanged(path); // recall same function for new path validation m_Controls.pythonEnvComboBox->insertItem(0, path); m_Controls.pythonEnvComboBox->setCurrentIndex(0); } } else if (!IsNNUNetInstalled(pyEnv)) { std::string warning = "WARNING: nnUNet is not detected on the Python environment you selected. Please select another " "environment or create one. For more info refer https://github.com/MIC-DKFZ/nnUNet"; ShowErrorMessage(warning); + DisableEverything(); + } + else + { + m_Controls.modeldirectoryBox->setEnabled(true); + m_Controls.previewButton->setEnabled(true); + m_Controls.refreshdirectoryBox->setEnabled(true); + m_Controls.multiModalBox->setEnabled(true); + QString setVal = FetchResultsFolderFromEnv(); + if (!setVal.isEmpty()) + { + m_Controls.modeldirectoryBox->setDirectory(setVal); + } + OnRefreshPresssed(); + m_PythonPath = pyEnv.mid(pyEnv.indexOf(" ") + 1); + if (!(m_PythonPath.endsWith("bin", Qt::CaseInsensitive) || m_PythonPath.endsWith("bin/", Qt::CaseInsensitive))) + { + m_PythonPath += QDir::separator() + QString("bin"); + } } } void QmitknnUNetToolGUI::OnCheckBoxChanged(int state) { bool visibility = false; if (state == Qt::Checked) { visibility = true; } ctkCheckBox *box = qobject_cast(sender()); if (box != nullptr) { - if (box->objectName() == QString("nopipBox")) - { - m_Controls.codedirectoryBox->setVisible(visibility); - m_Controls.nnUnetdirLabel->setVisible(visibility); - } - else if (box->objectName() == QString("multiModalBox")) + if (box->objectName() == QString("multiModalBox")) { m_Controls.multiModalSpinLabel->setVisible(visibility); m_Controls.multiModalSpinBox->setVisible(visibility); if (visibility) { QmitkDataStorageComboBox *defaultImage = new QmitkDataStorageComboBox(this, true); defaultImage->setObjectName(QString("multiModal_" + QString::number(0))); defaultImage->SetPredicate(m_MultiModalPredicate); mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); if (tool != nullptr) { defaultImage->SetDataStorage(tool->GetDataStorage()); defaultImage->SetSelectedNode(tool->GetRefNode()); } m_Controls.advancedSettingsLayout->addWidget(defaultImage, m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3); m_Modalities.push_back(defaultImage); - m_UI_ROWS++; } else { OnModalitiesNumberChanged(0); m_Controls.multiModalSpinBox->setValue(0); delete m_Modalities[0]; m_Modalities.pop_back(); + ClearAllModalLabels(); } } } } void QmitknnUNetToolGUI::OnModalitiesNumberChanged(int num) { while (num > static_cast(m_Modalities.size() - 1)) { QmitkDataStorageComboBox *multiModalBox = new QmitkDataStorageComboBox(this, true); mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); multiModalBox->SetDataStorage(tool->GetDataStorage()); multiModalBox->SetPredicate(m_MultiModalPredicate); multiModalBox->setObjectName(QString("multiModal_" + QString::number(m_Modalities.size() + 1))); m_Controls.advancedSettingsLayout->addWidget(multiModalBox, m_UI_ROWS + m_Modalities.size() + 1, 1, 1, 3); m_Modalities.push_back(multiModalBox); } while (num < static_cast(m_Modalities.size() - 1) && !m_Modalities.empty()) { QmitkDataStorageComboBox *child = m_Modalities.back(); if (child->objectName() == "multiModal_0") { std::iter_swap(m_Modalities.end() - 2, m_Modalities.end() - 1); child = m_Modalities.back(); } delete child; // delete the layout item m_Modalities.pop_back(); } m_Controls.advancedSettingsLayout->update(); } void QmitknnUNetToolGUI::AutoParsePythonPaths() { QString homeDir = QDir::homePath(); std::vector searchDirs; #ifdef _WIN32 searchDirs.push_back(QString("C:") + QDir::separator() + QString("ProgramData") + QDir::separator() + QString("anaconda3")); #else // Add search locations for possible standard python paths here searchDirs.push_back(homeDir + QDir::separator() + "environments"); searchDirs.push_back(homeDir + QDir::separator() + "anaconda3"); searchDirs.push_back(homeDir + QDir::separator() + "miniconda3"); searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "miniconda3"); searchDirs.push_back(homeDir + QDir::separator() + "opt" + QDir::separator() + "anaconda3"); #endif for (QString searchDir : searchDirs) { if (searchDir.endsWith("anaconda3", Qt::CaseInsensitive)) { if (QDir(searchDir).exists()) { m_Controls.pythonEnvComboBox->insertItem(0, "(base): " + searchDir); searchDir.append((QDir::separator() + QString("envs"))); } } for (QDirIterator subIt(searchDir, QDir::AllDirs, QDirIterator::NoIteratorFlags); subIt.hasNext();) { subIt.next(); QString envName = subIt.fileName(); if (!envName.startsWith('.')) // Filter out irrelevent hidden folders, if any. { m_Controls.pythonEnvComboBox->insertItem(0, "(" + envName + "): " + subIt.filePath()); } } } m_Controls.pythonEnvComboBox->setCurrentIndex(-1); } mitk::ModelParams QmitknnUNetToolGUI::MapToRequest(const QString &modelName, const QString &taskName, const QString &trainer, const QString &planId, const std::vector &folds) { mitk::ModelParams requestObject; requestObject.model = modelName.toStdString(); requestObject.trainer = trainer.toStdString(); requestObject.planId = planId.toStdString(); requestObject.task = taskName.toStdString(); requestObject.folds = folds; mitk::nnUNetTool::Pointer tool = this->GetConnectedToolAs(); requestObject.inputName = tool->GetRefNode()->GetName(); - requestObject.timeStamp = std::to_string(mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint()); + requestObject.timeStamp = + std::to_string(mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint()); return requestObject; } void QmitknnUNetToolGUI::SegmentationProcessFailed() { WriteErrorMessage( "STATUS: Error in the segmentation process.
No resulting segmentation can be loaded.
"); this->setCursor(Qt::ArrowCursor); std::stringstream stream; stream << "Error in the segmentation process. No resulting segmentation can be loaded."; ShowErrorMessage(stream.str()); - m_Controls.stopButton->setEnabled(false); } -void QmitknnUNetToolGUI::SegmentationResultHandler(mitk::nnUNetTool *tool) +void QmitknnUNetToolGUI::SegmentationResultHandler(mitk::nnUNetTool *tool, bool forceRender) { - tool->RenderOutputBuffer(); + if (forceRender) + { + tool->RenderOutputBuffer(); + } this->SetLabelSetPreview(tool->GetMLPreview()); - WriteStatusMessage("STATUS: Segmentation task finished successfully.
Please Confirm the " - "segmentation else, could result in data loss
"); - m_Controls.stopButton->setEnabled(false); + WriteStatusMessage("STATUS: Segmentation task finished successfully.
If multiple Preview objects are selected to Confirm, " + "they will be merged. Any unselected Preview objects will be lost.
"); } void QmitknnUNetToolGUI::ShowEnsembleLayout(bool visible) { if (m_EnsembleParams.empty()) { ctkCollapsibleGroupBox *groupBoxModel1 = new ctkCollapsibleGroupBox(this); auto lay1 = std::make_unique(groupBoxModel1); groupBoxModel1->setObjectName(QString::fromUtf8("model_1_Box")); groupBoxModel1->setTitle(QString::fromUtf8("Model 1")); groupBoxModel1->setMinimumSize(QSize(0, 0)); groupBoxModel1->setCollapsedHeight(5); groupBoxModel1->setCollapsed(false); groupBoxModel1->setFlat(true); groupBoxModel1->setAlignment(Qt::AlignRight); - m_Controls.advancedSettingsLayout->addWidget(groupBoxModel1, 4, 0, 1, 2); + m_Controls.advancedSettingsLayout->addWidget(groupBoxModel1, 5, 0, 1, 2); connect(lay1->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &))); connect( lay1->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &))); m_EnsembleParams.push_back(std::move(lay1)); - + ctkCollapsibleGroupBox *groupBoxModel2 = new ctkCollapsibleGroupBox(this); auto lay2 = std::make_unique(groupBoxModel2); groupBoxModel2->setObjectName(QString::fromUtf8("model_2_Box")); groupBoxModel2->setTitle(QString::fromUtf8("Model 2")); groupBoxModel2->setMinimumSize(QSize(0, 0)); groupBoxModel2->setCollapsedHeight(5); groupBoxModel2->setCollapsed(false); groupBoxModel2->setFlat(true); groupBoxModel2->setAlignment(Qt::AlignLeft); - m_Controls.advancedSettingsLayout->addWidget(groupBoxModel2, 4, 2, 1, 2); + m_Controls.advancedSettingsLayout->addWidget(groupBoxModel2, 5, 2, 1, 2); connect(lay2->modelBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnModelChanged(const QString &))); connect( lay2->plannerBox, SIGNAL(currentTextChanged(const QString &)), this, SLOT(OnTrainerChanged(const QString &))); m_EnsembleParams.push_back(std::move(lay2)); } for (auto &layout : m_EnsembleParams) { layout->setVisible(visible); } } diff --git a/Modules/SegmentationUI/files.cmake b/Modules/SegmentationUI/files.cmake index b3693314f8..17e542219b 100644 --- a/Modules/SegmentationUI/files.cmake +++ b/Modules/SegmentationUI/files.cmake @@ -1,94 +1,85 @@ set( CPP_FILES Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp Qmitk/QmitkAutoSegmentationToolGUIBase.cpp Qmitk/QmitkAutoMLSegmentationToolGUIBase.cpp Qmitk/QmitkBinaryThresholdToolGUIBase.cpp Qmitk/QmitkBinaryThresholdToolGUI.cpp Qmitk/QmitkBinaryThresholdULToolGUI.cpp Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.cpp Qmitk/QmitkConfirmSegmentationDialog.cpp Qmitk/QmitkCopyToClipBoardDialog.cpp Qmitk/QmitkDrawPaintbrushToolGUI.cpp Qmitk/QmitkErasePaintbrushToolGUI.cpp -Qmitk/QmitkFastMarchingToolGUIBase.cpp -Qmitk/QmitkFastMarchingTool3DGUI.cpp -Qmitk/QmitkFastMarchingToolGUI.cpp Qmitk/QmitkLiveWireTool2DGUI.cpp Qmitk/QmitkNewSegmentationDialog.cpp Qmitk/QmitkOtsuTool3DGUI.cpp Qmitk/QmitkPaintbrushToolGUI.cpp Qmitk/QmitkPickingToolGUI.cpp Qmitk/QmitkPixelManipulationToolGUI.cpp Qmitk/QmitkSlicesInterpolator.cpp Qmitk/QmitkToolGUI.cpp Qmitk/QmitkToolGUIArea.cpp Qmitk/QmitkToolSelectionBox.cpp -Qmitk/QmitkWatershedToolGUI.cpp Qmitk/QmitknnUNetToolGUI.cpp Qmitk/QmitknnUNetToolSlots.cpp #Added from ML Qmitk/QmitkLabelSetWidget.cpp Qmitk/QmitkSurfaceStampWidget.cpp Qmitk/QmitkMaskStampWidget.cpp Qmitk/QmitkSliceBasedInterpolatorWidget.cpp Qmitk/QmitkSurfaceBasedInterpolatorWidget.cpp Qmitk/QmitkSimpleLabelSetListWidget.cpp ) set(MOC_H_FILES Qmitk/QmitkAdaptiveRegionGrowingToolGUI.h Qmitk/QmitkAutoSegmentationToolGUIBase.h Qmitk/QmitkAutoMLSegmentationToolGUIBase.h Qmitk/QmitkBinaryThresholdToolGUIBase.h Qmitk/QmitkBinaryThresholdToolGUI.h Qmitk/QmitkBinaryThresholdULToolGUI.h Qmitk/QmitkCalculateGrayValueStatisticsToolGUI.h Qmitk/QmitkConfirmSegmentationDialog.h Qmitk/QmitkCopyToClipBoardDialog.h Qmitk/QmitkDrawPaintbrushToolGUI.h Qmitk/QmitkErasePaintbrushToolGUI.h -Qmitk/QmitkFastMarchingToolGUIBase.h -Qmitk/QmitkFastMarchingTool3DGUI.h -Qmitk/QmitkFastMarchingToolGUI.h Qmitk/QmitkLiveWireTool2DGUI.h Qmitk/QmitkNewSegmentationDialog.h Qmitk/QmitkOtsuTool3DGUI.h Qmitk/QmitkPaintbrushToolGUI.h Qmitk/QmitkPickingToolGUI.h Qmitk/QmitkPixelManipulationToolGUI.h Qmitk/QmitkSlicesInterpolator.h Qmitk/QmitkToolGUI.h Qmitk/QmitkToolGUIArea.h Qmitk/QmitkToolSelectionBox.h -Qmitk/QmitkWatershedToolGUI.h Qmitk/QmitknnUNetToolGUI.h Qmitk/QmitknnUNetGPU.h Qmitk/QmitknnUNetEnsembleLayout.h Qmitk/QmitknnUNetFolderParser.h #Added from ML Qmitk/QmitkLabelSetWidget.h Qmitk/QmitkSurfaceStampWidget.h Qmitk/QmitkMaskStampWidget.h Qmitk/QmitkSliceBasedInterpolatorWidget.h Qmitk/QmitkSurfaceBasedInterpolatorWidget.h Qmitk/QmitkSimpleLabelSetListWidget.h ) set(UI_FILES Qmitk/QmitkAdaptiveRegionGrowingToolGUIControls.ui Qmitk/QmitkConfirmSegmentationDialog.ui Qmitk/QmitkOtsuToolWidgetControls.ui Qmitk/QmitkLiveWireTool2DGUIControls.ui -Qmitk/QmitkWatershedToolGUIControls.ui #Added from ML Qmitk/QmitkLabelSetWidgetControls.ui Qmitk/QmitkSurfaceStampWidgetGUIControls.ui Qmitk/QmitkMaskStampWidgetGUIControls.ui Qmitk/QmitkSliceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitkSurfaceBasedInterpolatorWidgetGUIControls.ui Qmitk/QmitknnUNetToolGUIControls.ui ) set(QRC_FILES resources/SegmentationUI.qrc ) diff --git a/Modules/US/USFilters/mitkUSImageSource.cpp b/Modules/US/USFilters/mitkUSImageSource.cpp index 411a8bd76e..7221679399 100644 --- a/Modules/US/USFilters/mitkUSImageSource.cpp +++ b/Modules/US/USFilters/mitkUSImageSource.cpp @@ -1,121 +1,120 @@ /*============================================================================ 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 "mitkUSImageSource.h" #include "mitkProperties.h" const char* mitk::USImageSource::IMAGE_PROPERTY_IDENTIFIER = "id_nummer"; mitk::USImageSource::USImageSource() : m_OpenCVToMitkFilter(mitk::OpenCVToMitkImageFilter::New()), m_MitkToOpenCVFilter(nullptr), m_ImageFilter(mitk::BasicCombinationOpenCVImageFilter::New()), m_CurrentImageId(0) { } mitk::USImageSource::~USImageSource() { } void mitk::USImageSource::PushFilter(AbstractOpenCVImageFilter::Pointer filter) { m_ImageFilter->PushFilter(filter); } bool mitk::USImageSource::RemoveFilter(AbstractOpenCVImageFilter::Pointer filter) { return m_ImageFilter->RemoveFilter(filter); } bool mitk::USImageSource::GetIsFilterInThePipeline(AbstractOpenCVImageFilter::Pointer filter) { return m_ImageFilter->GetIsFilterOnTheList(filter); } std::vector mitk::USImageSource::GetNextImage() { std::vector result; // Apply OpenCV based filters beforehand if (m_ImageFilter.IsNotNull() && !m_ImageFilter->GetIsEmpty()) { std::vector imageVector; GetNextRawImage(imageVector); if(result.size() != imageVector.size()) result.resize(imageVector.size()); for (size_t i = 0; i < imageVector.size(); ++i) { if (!imageVector[i].empty()) { m_ImageFilterMutex.lock(); m_ImageFilter->FilterImage(imageVector[i], m_CurrentImageId); m_ImageFilterMutex.unlock(); // convert to MITK image this->m_OpenCVToMitkFilter->SetOpenCVMat(imageVector[i]); this->m_OpenCVToMitkFilter->Update(); // OpenCVToMitkImageFilter returns a standard mitk::image. result[i] = this->m_OpenCVToMitkFilter->GetOutput(); } } } else { this->GetNextRawImage(result); } for (size_t i = 0; i < result.size(); ++i) { if (result[i].IsNotNull()) { result[i]->SetProperty(IMAGE_PROPERTY_IDENTIFIER, mitk::IntProperty::New(m_CurrentImageId)); } else { - //MITK_WARN("mitkUSImageSource") << "Result image " << i << " is not set."; result[i] = mitk::Image::New(); } } m_CurrentImageId++; return result; } void mitk::USImageSource::GetNextRawImage(std::vector& imageVector) { // create filter object if it does not exist yet if (!m_MitkToOpenCVFilter) { m_MitkToOpenCVFilter = mitk::ImageToOpenCVImageFilter::New(); } // get mitk image through virtual method of the subclass std::vector mitkImg; this->GetNextRawImage(mitkImg); for (unsigned int i = 0; i < mitkImg.size(); ++i) { if (mitkImg[i].IsNull() || !mitkImg[i]->IsInitialized()) { imageVector[i] = cv::Mat(); } else { // convert mitk::Image to an OpenCV image m_MitkToOpenCVFilter->SetImage(mitkImg[i]); imageVector[i] = m_MitkToOpenCVFilter->GetOpenCVMat(); } } } diff --git a/Modules/US/USHardwareTelemed/CMakeLists.txt b/Modules/US/USHardwareTelemed/CMakeLists.txt index d97e99aaab..c728db930e 100644 --- a/Modules/US/USHardwareTelemed/CMakeLists.txt +++ b/Modules/US/USHardwareTelemed/CMakeLists.txt @@ -1,22 +1,18 @@ IF(WIN32) OPTION(MITK_USE_US_TELEMED_SDK "Enable support for Telemed api devices" OFF) IF(MITK_USE_US_TELEMED_SDK) -IF(CMAKE_CL_64) - message( FATAL_ERROR "The Telemed API cannot be used in 64 bit builds. Please configure a 32 bit build instead or deactivate MITK_USE_US_TELEMED_SDK." ) -ENDIF(CMAKE_CL_64) - SET(MITK_US_TELEMED_SDK_PATH "" CACHE PATH "Path to Telemed SDK header files.") MITK_CREATE_MODULE( DEPENDS MitkUS INCLUDE_DIRS "${MITK_US_TELEMED_SDK_PATH}" INTERNAL_INCLUDE_DIRS ${INCLUDE_DIRS_INTERNAL} AUTOLOAD_WITH MitkUS ) ENDIF(MITK_USE_US_TELEMED_SDK) ENDIF(WIN32) diff --git a/Modules/US/USHardwareTelemed/mitkUSTelemedDevice.cpp b/Modules/US/USHardwareTelemed/mitkUSTelemedDevice.cpp index 7fd2d11dad..fa2a0ce272 100644 --- a/Modules/US/USHardwareTelemed/mitkUSTelemedDevice.cpp +++ b/Modules/US/USHardwareTelemed/mitkUSTelemedDevice.cpp @@ -1,340 +1,362 @@ /*============================================================================ 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 "mitkUSTelemedDevice.h" - +#include "mitkImageReadAccessor.h" #include "mitkUSTelemedSDKHeader.h" mitk::USTelemedDevice::USTelemedDevice(std::string manufacturer, std::string model) : mitk::USDevice(manufacturer, model), m_ControlsProbes(mitk::USTelemedProbesControls::New(this)), m_ControlsBMode(mitk::USTelemedBModeControls::New(this)), m_ControlsDoppler(mitk::USTelemedDopplerControls::New(this)), m_ImageSource(mitk::USTelemedImageSource::New()), m_UsgMainInterface(0), m_Probe(0), m_UsgDataView(0), m_ProbesCollection(0) { - SetNumberOfOutputs(1); + SetNumberOfIndexedOutputs(1); SetNthOutput(0, this->MakeOutput(0)); } mitk::USTelemedDevice::~USTelemedDevice() { } std::string mitk::USTelemedDevice::GetDeviceClass() { return "org.mitk.modules.us.USTelemedDevice"; } mitk::USControlInterfaceBMode::Pointer mitk::USTelemedDevice::GetControlInterfaceBMode() { return m_ControlsBMode.GetPointer(); } mitk::USControlInterfaceProbes::Pointer mitk::USTelemedDevice::GetControlInterfaceProbes() { return m_ControlsProbes.GetPointer(); }; mitk::USControlInterfaceDoppler::Pointer mitk::USTelemedDevice::GetControlInterfaceDoppler() { return m_ControlsDoppler.GetPointer(); }; bool mitk::USTelemedDevice::OnInitialization() { CoInitialize(nullptr); // initialize COM library return true; } bool mitk::USTelemedDevice::OnConnection() { // create main Telemed API COM library object HRESULT hr; hr = CoCreateInstance(Usgfw2Lib::CLSID_Usgfw2, nullptr, CLSCTX_INPROC_SERVER, Usgfw2Lib::IID_IUsgfw2,(LPVOID*) &m_UsgMainInterface); if (FAILED(hr)) { SAFE_RELEASE(m_UsgMainInterface); MITK_ERROR("USDevice")("USTelemedDevice") << "Error at connecting to ultrasound device (" << hr << ")."; return false; } this->ConnectDeviceChangeSink(); return true; } bool mitk::USTelemedDevice::OnDisconnection() { // control objects cannot be active anymore m_ControlsBMode->SetIsActive(false); m_ControlsDoppler->SetIsActive(false); m_ControlsProbes->SetIsActive(false); ReleaseUsgControls(); return true; } bool mitk::USTelemedDevice::OnActivation() { // probe controls are available now m_ControlsProbes->SetIsActive(true); if ( m_ControlsProbes->GetProbesCount() < 1 ) { MITK_WARN("USDevice")("USTelemedDevice") << "No probe found."; return false; } // select first probe as a default m_ControlsProbes->SelectProbe(0); // set scan mode b as default for activation - // control interfaces can override this later HRESULT hr = m_UsgDataView->put_ScanMode(Usgfw2Lib::SCAN_MODE_B); if (FAILED(hr)) { MITK_ERROR("USDevice")("USTelemedDevice") << "Could not set scan mode b (" << hr << ")."; return false; } // start ultrasound scanning with selected scan mode hr = m_UsgDataView->put_ScanState(Usgfw2Lib::SCAN_STATE_RUN); if (FAILED(hr)) { MITK_ERROR("USDevice")("USTelemedDevice") << "Start scanning failed (" << hr << ")."; return false; } m_ControlsBMode->ReinitializeControls(); return true; } bool mitk::USTelemedDevice::OnDeactivation() { this->StopScanning(); return true; } void mitk::USTelemedDevice::OnFreeze(bool freeze) { if ( freeze ) { m_UsgDataView->put_ScanState(Usgfw2Lib::SCAN_STATE_FREEZE); } else { m_UsgDataView->put_ScanState(Usgfw2Lib::SCAN_STATE_RUN); } } +void mitk::USTelemedDevice::GenerateData() +{ + mitk::USTelemedImageSource::Pointer s = dynamic_cast(GetUSImageSource().GetPointer()); + s->GetNextRawImage(m_ImageVector); + Superclass::GenerateData(); +} + mitk::USImageSource::Pointer mitk::USTelemedDevice::GetUSImageSource() { return m_ImageSource.GetPointer(); } void mitk::USTelemedDevice::ReleaseUsgControls() { if (m_UsgDataView) { this->StopScanning(); }; SAFE_RELEASE(m_UsgMainInterface); SAFE_RELEASE(m_Probe); SAFE_RELEASE(m_UsgDataView); SAFE_RELEASE(m_ProbesCollection); } void mitk::USTelemedDevice::StopScanning() { if ( ! m_UsgDataView ) { MITK_WARN("USDevice")("USTelemedDevice") << "Cannot stop scanning as Telemed Data View is null."; return; } HRESULT hr; hr = m_UsgDataView->put_ScanState(Usgfw2Lib::SCAN_STATE_STOP); if (FAILED(hr)) { MITK_ERROR("USDevice")("USTelemedDevice") << "Stop scanning failed (" << hr << ")."; mitkThrow() << "Stop scanning failed (" << hr << ")."; } } +std::vector mitk::USTelemedDevice::GetAllProbes(){ + return m_ControlsProbes->GetProbeSet(); +} + +mitk::USProbe::Pointer mitk::USTelemedDevice::GetCurrentProbe(){ + return m_ControlsProbes->GetSelectedProbe(); +} + +mitk::USProbe::Pointer mitk::USTelemedDevice::GetProbeByName(std::string name){ + for (mitk::USProbe::Pointer p : m_ControlsProbes->GetProbeSet()){ + if (p->GetName().compare(name)==0) {return p;} + } + return nullptr; +} + Usgfw2Lib::IUsgfw2* mitk::USTelemedDevice::GetUsgMainInterface() { return m_UsgMainInterface; } void mitk::USTelemedDevice::SetActiveDataView(Usgfw2Lib::IUsgDataView* usgDataView) { // do nothing if the usg data view hasn't changed if ( m_UsgDataView != usgDataView ) { // scan converter plugin is connected to IUsgDataView -> a new plugin // must be created when changing IUsgDataView m_UsgDataView = usgDataView; if ( ! m_ImageSource->CreateAndConnectConverterPlugin(m_UsgDataView, Usgfw2Lib::SCAN_MODE_B)) { return; } // b mode control object must know about active data view m_ControlsBMode->SetUsgDataView(m_UsgDataView); } } void mitk::USTelemedDevice::ConnectDeviceChangeSink( ) { IConnectionPointContainer* cpc = nullptr; HRESULT hr = m_UsgMainInterface->QueryInterface(IID_IConnectionPointContainer, (void**)&cpc); if (hr != S_OK) cpc = nullptr; if (cpc != nullptr) hr = cpc->FindConnectionPoint(Usgfw2Lib::IID_IUsgDeviceChangeSink, &m_UsgDeviceChangeCpnt); if (hr != S_OK) { m_UsgDeviceChangeCpnt = nullptr; m_UsgDeviceChangeCpntCookie = 0; } SAFE_RELEASE(cpc); if (m_UsgDeviceChangeCpnt != nullptr) hr = m_UsgDeviceChangeCpnt->Advise((IUnknown*)((Usgfw2Lib::IUsgDeviceChangeSink*)this), &m_UsgDeviceChangeCpntCookie); } // --- Methods for Telemed API Interfaces HRESULT __stdcall mitk::USTelemedDevice::raw_OnBeamformerArrive(IUnknown *pUsgBeamformer, ULONG *reserved) { this->Connect(); return S_OK; } HRESULT __stdcall mitk::USTelemedDevice::raw_OnBeamformerRemove(IUnknown *pUsgBeamformer, ULONG *reserved) { if ( this->GetIsActive() ) { this->Deactivate(); } this->Disconnect(); return S_OK; } HRESULT __stdcall mitk::USTelemedDevice::raw_OnProbeArrive(IUnknown*, ULONG* probeIndex) { m_ControlsProbes->ProbeAdded(static_cast(*probeIndex)); this->Activate(); return S_OK; }; HRESULT __stdcall mitk::USTelemedDevice::raw_OnProbeRemove(IUnknown*, ULONG* probeIndex) { m_ControlsProbes->ProbeRemoved(static_cast(*probeIndex)); if ( this->GetIsActive() ) { this->Deactivate(); } return S_OK; }; STDMETHODIMP_(ULONG) mitk::USTelemedDevice::AddRef() { ++m_RefCount; return m_RefCount; } STDMETHODIMP_(ULONG) mitk::USTelemedDevice::Release() { --m_RefCount; return m_RefCount; } STDMETHODIMP mitk::USTelemedDevice::QueryInterface(REFIID riid, void** ppv) { if (riid == IID_IUnknown || riid == Usgfw2Lib::IID_IUsgDeviceChangeSink) { *ppv = (IUsgDeviceChangeSink*)this; return S_OK; } if (riid == IID_IDispatch) { *ppv = (IDispatch*)this; return S_OK; } return E_NOINTERFACE; } HRESULT mitk::USTelemedDevice::GetTypeInfoCount(UINT *pctinfo) { if (pctinfo == nullptr) return E_INVALIDARG; *pctinfo = 0; return S_OK; } HRESULT mitk::USTelemedDevice::GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo) { if (pptinfo == nullptr) return E_INVALIDARG; *pptinfo = nullptr; if(itinfo != 0) return DISP_E_BADINDEX; return S_OK; } HRESULT mitk::USTelemedDevice::GetIDsOfNames(const IID &riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid) { // this is not used - must use the same fixed dispid's from Usgfw2 idl file return S_OK; } HRESULT mitk::USTelemedDevice::Invoke(DISPID dispIdMember, const IID &riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { if ( (dispIdMember >= 1) && (dispIdMember <= 6) ) { if (pDispParams->cArgs != 2) // we need 2 arguments return S_OK; IUnknown *unkn = nullptr; ULONG *res = nullptr; VARIANTARG* p1; VARIANTARG* p; p1 = pDispParams->rgvarg; p = p1; if (p->vt == (VT_BYREF|VT_UI4)) res = p->pulVal; p1++; p = p1; if (p->vt == VT_UNKNOWN) unkn = (IUnknown*)(p->punkVal); if (dispIdMember == 1) OnProbeArrive(unkn, res); else if (dispIdMember == 2) OnBeamformerArrive(unkn, res); else if (dispIdMember == 3) OnProbeRemove(unkn, res); else if (dispIdMember == 4) OnBeamformerRemove(unkn, res); else if (dispIdMember == 5) OnProbeStateChanged(unkn, res); else if (dispIdMember == 6) OnBeamformerStateChanged(unkn, res); } return S_OK; } diff --git a/Modules/US/USHardwareTelemed/mitkUSTelemedDevice.h b/Modules/US/USHardwareTelemed/mitkUSTelemedDevice.h index 36ffefa0ac..f5d9f03e45 100644 --- a/Modules/US/USHardwareTelemed/mitkUSTelemedDevice.h +++ b/Modules/US/USHardwareTelemed/mitkUSTelemedDevice.h @@ -1,181 +1,207 @@ /*============================================================================ 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 MITKUSTelemedDevice_H_HEADER_INCLUDED_ #define MITKUSTelemedDevice_H_HEADER_INCLUDED_ #include "mitkUSDevice.h" #include "mitkUSTelemedImageSource.h" #include "mitkUSTelemedScanConverterPlugin.h" #include "mitkUSTelemedProbesControls.h" #include "mitkUSTelemedBModeControls.h" #include "mitkUSTelemedDopplerControls.h" #include "mitkUSTelemedSDKHeader.h" namespace mitk { /** * \brief Implementation of mitk::USDevice for Telemed API devices. * Connects to a Telemed API device through its COM library interface. * * This class handles all API communications and creates interfaces for * b mode, doppler and probes controls. * Images given by the device are put into an object of * mitk::USTelemedImageSource. * * It implements IUsgDeviceChangeSink of the Telemed API to be notified * of changes to beamformer device or probes (e.g. probe change). */ class USTelemedDevice : public USDevice, public Usgfw2Lib::IUsgDeviceChangeSink { public: mitkClassMacro(USTelemedDevice, mitk::USDevice); mitkNewMacro2Param(Self, std::string, std::string); /** * \brief Returns the class of the device. */ virtual std::string GetDeviceClass(); virtual USControlInterfaceBMode::Pointer GetControlInterfaceBMode(); virtual USControlInterfaceProbes::Pointer GetControlInterfaceProbes(); virtual USControlInterfaceDoppler::Pointer GetControlInterfaceDoppler(); /** * \brief Is called during the initialization process. * There is nothing done on the initialization of a mik::USTelemedDevive object. * * \return always true */ virtual bool OnInitialization(); /** * \brief Is called during the connection process. * Connect to the Telemed API and try to get available probes from the device. * * \return true if successfull, false if no device is connected to the pc * \throws mitk::Exception if something goes wrong at the API calls */ virtual bool OnConnection(); /** * \brief Is called during the disconnection process. * Deactivate and remove all Telemed API controls. A disconnect from the * Telemed API is not possible for which reason the hardware stays in connected * state even after calling this method. * * \return always true * \throws mitk::Exception if something goes wrong at the API calls */ virtual bool OnDisconnection(); /** * \brief Is called during the activation process. * After this method is finished, the device is generating images in b mode. * Changing scanning mode is possible afterwards by using the appropriate * control interfaces. * * \return always true * \throws mitk::Exception if something goes wrong at the API calls */ virtual bool OnActivation(); /** * \brief Is called during the deactivation process. * After a call to this method the device is connected, but not producing images anymore. * * \return always true * \throws mitk::Exception if something goes wrong at the API calls */ virtual bool OnDeactivation(); /** * \brief Changes scan state of the device if freeze is toggeled in mitk::USDevice. */ virtual void OnFreeze(bool freeze); /** @return Returns the current image source of this device. */ USImageSource::Pointer GetUSImageSource( ); + /** + * \brief Returns all probes for this device or an empty vector it no probes were set + * Returns a std::vector of all probes that exist for this device if there were probes set while creating or modifying this USVideoDevice. + * Otherwise it returns an empty vector. Therefore always check if vector is filled, before using it! + */ + std::vector GetAllProbes(); + + /** + * \brief Return current active probe for this USDevice + * Returns a pointer to the probe that is currently in use. If there were probes set while creating or modifying this USDevice. + * Returns null otherwise + */ + mitk::USProbe::Pointer GetCurrentProbe(); + + /** + * \brief get the probe by its name + * Returns a pointer to the probe identified by the given name. If no probe of given name exists for this Device 0 is returned. + */ + mitk::USProbe::Pointer GetProbeByName(std::string name); + + /** + * \brief Grabs the next frame from the Video input. + * This method is called internally, whenever Update() is invoked by an Output. + */ + void GenerateData() override; + /** * \brief Getter for main Telemed API object. * This method is for being called by Telemed control interfaces. */ Usgfw2Lib::IUsgfw2* GetUsgMainInterface(); /** * \brief Changes active IUsgDataView of the device. * This method is for being called by Telemed control interfaces. */ void SetActiveDataView(Usgfw2Lib::IUsgDataView*); // Methods implemented for IUsgDeviceChangeSink virtual HRESULT __stdcall raw_OnProbeArrive(IUnknown *pUsgProbe, ULONG *reserved); virtual HRESULT __stdcall raw_OnBeamformerArrive(IUnknown *pUsgBeamformer, ULONG *reserved); virtual HRESULT __stdcall raw_OnProbeRemove(IUnknown *pUsgProbe, ULONG *reserved); virtual HRESULT __stdcall raw_OnBeamformerRemove(IUnknown *pUsgBeamformer, ULONG *reserved); virtual HRESULT __stdcall raw_OnProbeStateChanged(IUnknown *pUsgProbe, ULONG *reserved) { return S_OK; }; virtual HRESULT __stdcall raw_OnBeamformerStateChanged(IUnknown *pUsgBeamformer, ULONG *reserved) { return S_OK; }; // Methods implemented for IUnknown (necessary for IUsgDeviceChangeSink) STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release(); STDMETHODIMP QueryInterface(REFIID riid, void** ppv); // Methods implemented for IDispatch (necessary for IUsgDeviceChangeSink) virtual HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *pctinfo); virtual HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo** pptinfo); virtual HRESULT STDMETHODCALLTYPE GetIDsOfNames(const IID &riid, LPOLESTR* rgszNames, UINT cNames, LCID lcid, DISPID* rgdispid); virtual HRESULT STDMETHODCALLTYPE Invoke(DISPID dispIdMember, const IID &riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr); protected: /** * Constructs a mitk::USTelemedDevice object by given manufacturer * and model string. These strings are just for labeling the device * in the micro service. * * Control interfaces and image source are available directly after * construction. Registration at the micro service happens not before * initialization method was called. */ USTelemedDevice(std::string manufacturer, std::string model); virtual ~USTelemedDevice(); void ReleaseUsgControls( ); void ConnectDeviceChangeSink( ); /** * \brief Stop ultrasound scanning by Telemed API call. * * \throw mitk::Exception if API call returned with an error */ void StopScanning( ); USTelemedProbesControls::Pointer m_ControlsProbes; USTelemedBModeControls::Pointer m_ControlsBMode; USTelemedDopplerControls::Pointer m_ControlsDoppler; USTelemedImageSource::Pointer m_ImageSource; Usgfw2Lib::IUsgfw2* m_UsgMainInterface; Usgfw2Lib::IProbe* m_Probe; Usgfw2Lib::IUsgDataView* m_UsgDataView; Usgfw2Lib::IUsgCollection* m_ProbesCollection; ULONG m_RefCount; IConnectionPoint* m_UsgDeviceChangeCpnt; DWORD m_UsgDeviceChangeCpntCookie; }; } // namespace mitk #endif // MITKUSTelemedDevice_H_HEADER_INCLUDED_ diff --git a/Modules/US/USHardwareTelemed/mitkUSTelemedImageSource.cpp b/Modules/US/USHardwareTelemed/mitkUSTelemedImageSource.cpp index 1e2d974945..c13bb8a2dd 100644 --- a/Modules/US/USHardwareTelemed/mitkUSTelemedImageSource.cpp +++ b/Modules/US/USHardwareTelemed/mitkUSTelemedImageSource.cpp @@ -1,141 +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 "mitkUSTelemedImageSource.h" #include "mitkUSTelemedSDKHeader.h" #include "MITKUSTelemedScanConverterPlugin.h" #include "mitkImageReadAccessor.h" mitk::USTelemedImageSource::USTelemedImageSource() : m_Image(mitk::Image::New()), - m_ImageMutex(itk::FastMutexLock::New()), + m_ImageMutex(new std::mutex()), m_Plugin(0), m_PluginCallback(0), m_UsgDataView(0), m_ImageProperties(0), m_DepthProperties(0), m_OldnXPelsPerUnit(0), m_OldnYPelsPerUnit(0) { } mitk::USTelemedImageSource::~USTelemedImageSource( ) { SAFE_RELEASE(m_PluginCallback); SAFE_RELEASE(m_Plugin); SAFE_RELEASE(m_ImageProperties); SAFE_RELEASE(m_DepthProperties); + delete m_ImageMutex; } void mitk::USTelemedImageSource::GetNextRawImage(std::vector& imageVector) { if (imageVector.empty() ) { imageVector.push_back( mitk::Image::New()); } //get the actual resolution to check if it changed. We have to do this every time because the geometry takes a few frames to adapt Usgfw2Lib::tagImageResolution resolutionInMetersActual; m_ImageProperties->GetResolution(&resolutionInMetersActual, 0); if (m_OldnXPelsPerUnit != resolutionInMetersActual.nXPelsPerUnit || m_OldnYPelsPerUnit != resolutionInMetersActual.nYPelsPerUnit) { //we can only update if the image exists and has a geometry if (m_Image.IsNotNull() && m_Image->GetGeometry() != nullptr) { m_OldnXPelsPerUnit = resolutionInMetersActual.nXPelsPerUnit; m_OldnYPelsPerUnit = resolutionInMetersActual.nYPelsPerUnit; UpdateImageGeometry(); } } //now update image if ( m_Image->IsInitialized() ) { - m_ImageMutex->Lock(); + m_ImageMutex->lock(); // copy contents of the given image into the member variable imageVector.at(0)->Initialize(m_Image->GetPixelType(), m_Image->GetDimension(), m_Image->GetDimensions()); mitk::ImageReadAccessor inputReadAccessor(m_Image, m_Image->GetSliceData(0,0,0)); imageVector.at(0)->SetSlice(inputReadAccessor.GetData()); imageVector.at(0)->SetGeometry(m_Image->GetGeometry()); - - m_ImageMutex->Unlock(); + m_ImageMutex->unlock(); } - } void mitk::USTelemedImageSource::UpdateImageGeometry() { Usgfw2Lib::tagPixelsOrigin origin = Usgfw2Lib::tagPixelsOrigin(); Usgfw2Lib::tagImageResolution resolutionInMeters; m_ImageProperties->GetResolution(&resolutionInMeters,0); mitk::Vector3D spacing; spacing[0] = ((double)1 / resolutionInMeters.nXPelsPerUnit) * 1000; //conversion: meters to millimeters spacing[1] = ((double)1 / resolutionInMeters.nXPelsPerUnit) * 1000; //conversion: meters to millimeters spacing[2] = 1; - m_ImageMutex->Lock(); + m_ImageMutex->lock(); if(m_Image.IsNotNull() && (m_Image->GetGeometry()!=nullptr)) { m_Image->GetGeometry()->SetSpacing(spacing); m_Image->GetGeometry()->Modified(); } else {MITK_WARN << "image or geometry was nullptr, can't adapt geometry";} - m_ImageMutex->Unlock(); + m_ImageMutex->unlock(); MITK_DEBUG << "UpdateImageGeometry called!"; MITK_DEBUG << "depth: " << m_DepthProperties->GetCurrent(); MITK_DEBUG << "new spacing: " << spacing; } bool mitk::USTelemedImageSource::CreateAndConnectConverterPlugin(Usgfw2Lib::IUsgDataView* usgDataView, Usgfw2Lib::tagScanMode scanMode) { IUnknown* tmp_obj = nullptr; - // create control object from Telemed API mitk::telemed::CreateUsgControl( usgDataView, Usgfw2Lib::IID_IUsgScanConverterPlugin, scanMode, 0, (void**)&tmp_obj ); if ( ! tmp_obj ) { MITK_ERROR("USImageSource")("USTelemedImageSource") << "Could not create scan converter plugin."; return false; } // create the callback object for the scan conversion if ( ! m_PluginCallback ) { m_PluginCallback = new USTelemedScanConverterPlugin(); // current image buffer should be copied to m_Image at every callback m_PluginCallback->SetOutputImage(m_Image.GetPointer(), m_ImageMutex); } else { // make sure that the scan converter plugin is not set // to the plugin callback any longer m_PluginCallback->SetScanConverterPlugin(0); } // now the ScanConverterPlugin can be created and set as plugin SAFE_RELEASE(m_Plugin); m_Plugin = (Usgfw2Lib::IUsgScanConverterPlugin*)tmp_obj; m_PluginCallback->SetScanConverterPlugin(m_Plugin); //last: create some connections which are needed inside this class for communication with the telemed device m_UsgDataView = usgDataView; // create telemed controls if (!m_DepthProperties) {CREATE_TelemedControl(m_DepthProperties, m_UsgDataView, Usgfw2Lib::IID_IUsgDepth, Usgfw2Lib::IUsgDepth, Usgfw2Lib::SCAN_MODE_B);} if (!m_ImageProperties) {CREATE_TelemedControl(m_ImageProperties, m_UsgDataView, Usgfw2Lib::IID_IUsgImageProperties, Usgfw2Lib::IUsgImageProperties, Usgfw2Lib::SCAN_MODE_B);} return true; } diff --git a/Modules/US/USHardwareTelemed/mitkUSTelemedImageSource.h b/Modules/US/USHardwareTelemed/mitkUSTelemedImageSource.h index c66ad171d5..b8f5f4bd4c 100644 --- a/Modules/US/USHardwareTelemed/mitkUSTelemedImageSource.h +++ b/Modules/US/USHardwareTelemed/mitkUSTelemedImageSource.h @@ -1,79 +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 MITKUSTelemedImageSource_H_HEADER_INCLUDED_ #define MITKUSTelemedImageSource_H_HEADER_INCLUDED_ #include "mitkUSImageSource.h" #include "mitkUSTelemedSDKHeader.h" #include "mitkUSTelemedScanConverterPlugin.h" -#include "itkFastMutexLock.h" - namespace mitk { /** * \brief Implementation of mitk::USImageSource for Telemed API devices. * The method mitk::USImageSource::GetNextRawImage() is implemented for * getting images from the Telemed API. * * A method for connecting this ImageSource to the Telemed API is * implemented (mitk::USTelemedImageSource::CreateAndConnectConverterPlugin()). * This method is available for being used by mitk::USTelemedDevice. */ class USTelemedImageSource : public USImageSource { public: mitkClassMacro(USTelemedImageSource, USImageSource); itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * Implementation of the superclass method. Returns the pointer * to the mitk::Image filled by Telemed API callback. */ virtual void GetNextRawImage( std::vector& ); /** * Updates the geometry of the current image from the API. * Is used internally when a new image is initialized, but * also needs to be called if, e.g., the zoom factor is changed. */ void UpdateImageGeometry(); /** * \brief Connect this object to the Telemed API. * This method is for being used by mitk::USTelemedDevice. */ bool CreateAndConnectConverterPlugin( Usgfw2Lib::IUsgDataView*, Usgfw2Lib::tagScanMode ); protected: USTelemedImageSource( ); virtual ~USTelemedImageSource( ); Usgfw2Lib::IUsgScanConverterPlugin* m_Plugin; USTelemedScanConverterPlugin* m_PluginCallback; Usgfw2Lib::IUsgDataView* m_UsgDataView; // main SDK object for comminucating with the Telemed API //API objects for communication, used to get the right geometry Usgfw2Lib::IUsgImageProperties* m_ImageProperties; Usgfw2Lib::IUsgDepth* m_DepthProperties; long m_OldnXPelsPerUnit; long m_OldnYPelsPerUnit; - mitk::Image::Pointer m_Image; - itk::FastMutexLock::Pointer m_ImageMutex; + mitk::Image::Pointer m_Image; + std::mutex* m_ImageMutex; }; } // namespace mitk #endif // MITKUSTelemedImageSource_H diff --git a/Modules/US/USHardwareTelemed/mitkUSTelemedProbesControls.cpp b/Modules/US/USHardwareTelemed/mitkUSTelemedProbesControls.cpp index 36c643c832..a795c380f0 100644 --- a/Modules/US/USHardwareTelemed/mitkUSTelemedProbesControls.cpp +++ b/Modules/US/USHardwareTelemed/mitkUSTelemedProbesControls.cpp @@ -1,243 +1,243 @@ /*============================================================================ 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 "mitkUSTelemedProbesControls.h" #include "mitkUSTelemedDevice.h" #include mitk::USTelemedProbesControls::USTelemedProbesControls(itk::SmartPointer device) : mitk::USControlInterfaceProbes(device.GetPointer()), m_IsActive(false), m_TelemedDevice(device), m_ProbesCollection(0), m_Probe(0) { } mitk::USTelemedProbesControls::~USTelemedProbesControls() { SAFE_RELEASE(m_ProbesCollection); } void mitk::USTelemedProbesControls::SetIsActive(bool isActive) { if ( ! m_TelemedDevice ) { MITK_WARN("USTelemedProbesControls")("USControlInterfaceProbes") << "Cannot activate probe controls while device is not set."; return; } if ( isActive && m_ProbesCollection == 0 ) { this->CreateProbesCollection(); this->CreateProbesSet(); } else { } m_IsActive = isActive; } bool mitk::USTelemedProbesControls::GetIsActive() { return m_IsActive; } std::vector mitk::USTelemedProbesControls::GetProbeSet() { // create a new vector of base class (USProbe) objects, because // interface wants a vector of this type - std::vector usProbes(m_ProbesSet.size(), 0); + std::vector usProbes; for (unsigned int n = 0; n < m_ProbesSet.size(); ++n) { - usProbes.at(n) = m_ProbesSet.at(n).GetPointer(); + usProbes.push_back(m_ProbesSet.at(n).GetPointer()); } return usProbes; } void mitk::USTelemedProbesControls::OnSelectProbe(unsigned int index) { if (index >= m_ProbesSet.size()) { MITK_ERROR("USTelemedProbesControls")("USControlInterfaceProbes") << "Cannot select probe with index " << index << ". Maximum possible index is " << m_ProbesSet.size()-1 << "."; mitkThrow() << "Cannot select probe with index " << index << ". Maximum possible index is " << m_ProbesSet.size()-1 << "."; } m_TelemedDevice->SetActiveDataView(m_ProbesSet.at(index)->GetUsgDataView()); m_SelectedProbeIndex = index; } void mitk::USTelemedProbesControls::OnSelectProbe(mitk::USProbe::Pointer probe) { } mitk::USProbe::Pointer mitk::USTelemedProbesControls::GetSelectedProbe() { if (m_SelectedProbeIndex >= m_ProbesSet.size()) { MITK_ERROR("USTelemedProbesControls")("USControlInterfaceProbes") << "Cannot get active probe as the current index is" << m_SelectedProbeIndex << ". Maximum possible index is " << m_ProbesSet.size()-1 << "."; mitkThrow() << "Cannot get active probe as the current index is" << m_SelectedProbeIndex << ". Maximum possible index is " << m_ProbesSet.size()-1 << "."; } return m_ProbesSet.at(m_SelectedProbeIndex).GetPointer(); } unsigned int mitk::USTelemedProbesControls::GetProbesCount() const { return m_ProbesSet.size(); } /*void mitk::USTelemedProbesControls::SetTelemedDevice(itk::SmartPointer telemedDevice) { m_TelemedDevice = telemedDevice; }*/ void mitk::USTelemedProbesControls::ProbeRemoved(unsigned int index) { MITK_INFO << "Probe removed..."; if ( m_ProbesSet.size() > index ) { m_ProbesSet.erase(m_ProbesSet.begin() + index); } } void mitk::USTelemedProbesControls::ProbeAdded(unsigned int index) { MITK_INFO << "Probe arrived..."; this->CreateProbesCollection(); this->CreateProbesSet(); // Activate the added probe, if the added probe is the first probe if (m_ProbesSet.size() == 1) { m_TelemedDevice->SetActiveDataView(m_ProbesSet.at(0)->GetUsgDataView()); } } bool mitk::USTelemedProbesControls::CreateProbesCollection() { IUnknown* tmp_obj = nullptr; HRESULT hr; // get the main API interface from the Telemed device Usgfw2Lib::IUsgfw2* usgMainInterface = m_TelemedDevice->GetUsgMainInterface(); if ( ! usgMainInterface ) { MITK_ERROR("USTelemedProbesControls")("USControlInterfaceProbes") << "Main interface of Telemed device must not be null."; mitkThrow() << "Main interface of Telemed device must not be null."; } // get probes collection from Telemed API hr = usgMainInterface->get_ProbesCollection(&tmp_obj); if (FAILED(hr) || ! tmp_obj) { MITK_WARN("USTelemedProbesControls")("USControlInterfaceProbes") << "Error on getting probes collection (" << hr << ")."; return false; } // second step for getting probes collection from Telemed API SAFE_RELEASE(m_ProbesCollection); hr = tmp_obj->QueryInterface(Usgfw2Lib::IID_IUsgCollection,(void**)&m_ProbesCollection); SAFE_RELEASE(tmp_obj); if (FAILED(hr) || ! m_ProbesCollection) { MITK_WARN("USTelemedProbesControls")("USControlInterfaceProbes") << "Error on querying interface for probes collection (" << hr << ")."; return false; } return true; } void mitk::USTelemedProbesControls::CreateProbesSet() { if ( ! m_ProbesCollection) { MITK_ERROR("USTelemedProbesControls")("USControlInterfaceProbes") << "Cannot get probe set without ProbesCollection being initialized before."; mitkThrow() << "Cannot get probe set without ProbesCollection being initialized before."; } // get number of available probes LONG probes_count = 0; HRESULT hr = m_ProbesCollection->get_Count(&probes_count); if (FAILED(hr)) { mitkThrow() << "Could not get probes count (" << hr << ")."; } if ( ! m_TelemedDevice ) { MITK_ERROR("USTelemedProbesControls")("USControlInterfaceProbes") << "Telemed device must not be null when creating probes set."; mitkThrow() << "Telemed device must not be null when creating probes set."; } // get the main API interface from the Telemed device Usgfw2Lib::IUsgfw2* usgMainInterface = m_TelemedDevice->GetUsgMainInterface(); if ( ! usgMainInterface ) { MITK_ERROR("USTelemedProbesControls")("USControlInterfaceProbes") << "Usg main interface must not be null when creating probes set."; mitkThrow() << "Usg main interface must not be null when creating probes set."; } // initialize probes set with new vector - m_ProbesSet = std::vector(probes_count, 0); + m_ProbesSet = std::vector(); for (LONG n = 0; n < probes_count; ++n) { // get the probe item from the API collection IUnknown* tmp_obj = nullptr; hr = m_ProbesCollection->Item(n,&tmp_obj); if (FAILED(hr)) { MITK_ERROR("USTelemedProbesControls")("USControlInterfaceProbes") << "Could not get probe with index " << n << "."; mitkThrow() << "Could not get probe with index " << n << "."; } // convert this item to a probe Usgfw2Lib::IProbe* probe; hr = tmp_obj->QueryInterface(Usgfw2Lib::IID_IProbe,(void**)&probe); if (FAILED(hr)) { MITK_ERROR("USTelemedProbesControls")("USControlInterfaceProbes") << "Error on querying interface for probe with index "<< n << "."; mitkThrow() << "Error on querying interface for probe with index "<< n << "."; } // create main ultrasound scanning object for selected probe Usgfw2Lib::IUsgDataView* usgDataView; Usgfw2Lib::IUsgDataViewPtr usgDataViewTmp; usgDataViewTmp = usgMainInterface->CreateDataView(probe); usgDataViewTmp->QueryInterface(Usgfw2Lib::IID_IUsgDataView, (void**)&usgDataView); if (! usgDataView) { MITK_ERROR("USTelemedProbesControls")("USControlInterfaceProbes") << "Could not create data view for selected probe."; mitkThrow() << "Could not create data view for selected probe."; } // probe object can be created now from API data - m_ProbesSet.at(n) = mitk::USTelemedProbe::New(probe, usgDataView); + m_ProbesSet.push_back(mitk::USTelemedProbe::New(probe, usgDataView)); SAFE_RELEASE(tmp_obj); } } diff --git a/Modules/US/USHardwareTelemed/mitkUSTelemedScanConverterPlugin.cpp b/Modules/US/USHardwareTelemed/mitkUSTelemedScanConverterPlugin.cpp index ad27c0edc0..597364bf53 100644 --- a/Modules/US/USHardwareTelemed/mitkUSTelemedScanConverterPlugin.cpp +++ b/Modules/US/USHardwareTelemed/mitkUSTelemedScanConverterPlugin.cpp @@ -1,149 +1,147 @@ /*============================================================================ 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 "mitkUSTelemedScanConverterPlugin.h" #include "mitkImageWriteAccessor.h" USTelemedScanConverterPlugin::USTelemedScanConverterPlugin( ) - : m_Plugin(nullptr), m_OutputImage(nullptr), m_OutputImageMutex(nullptr) + : m_Plugin(nullptr), m_OutputImage(nullptr) { } USTelemedScanConverterPlugin::~USTelemedScanConverterPlugin( ) { ReleasePlugin(); } // -- internal Telemed API function HRESULT __stdcall USTelemedScanConverterPlugin::QueryInterface(const IID& iid, void** ppv) { reinterpret_cast(*ppv)->AddRef() ; return S_OK ; } // -- internal Telemed API function ULONG __stdcall USTelemedScanConverterPlugin::AddRef() { return InterlockedIncrement(&m_cRef) ; } // -- internal Telemed API function ULONG __stdcall USTelemedScanConverterPlugin::Release() { if (InterlockedDecrement(&m_cRef) == 0) { delete this ; return 0 ; } return m_cRef ; } STDMETHODIMP USTelemedScanConverterPlugin::InterimOutBufferCB ( PBYTE pBufferInterim, int nInterimBufferLen, PBYTE pBufferOut, int nOutBufferLen, int nOutX1, int nOutY1, int nOutX2, int nOutY2 ) { if ( m_OutputImage.IsNull() ) { return S_FALSE; }; - - if ( m_OutputImageMutex.IsNotNull() ) { m_OutputImageMutex->Lock(); } - + m_OutputImageMutex->lock(); // initialize mitk::Image with given image size on the first time if ( ! m_OutputImage->IsInitialized() ) { unsigned int dim[]={static_cast(abs(nOutX2 - nOutX1)), static_cast(abs(nOutY2 - nOutY1))}; // image dimensions m_OutputImage->Initialize(mitk::MakeScalarPixelType(), 2, dim); } // lock the image for writing an copy the given buffer into the image then m_OutputImage->SetSlice(pBufferOut); - if ( m_OutputImageMutex.IsNotNull() ) { m_OutputImageMutex->Unlock(); } + m_OutputImageMutex->unlock(); return S_OK; } void USTelemedScanConverterPlugin::ReleasePlugin() { if (m_Plugin != nullptr) { // remove this callback from Telemed API plugin m_Plugin->SetCallback(nullptr,USPC_BUFFER_INTERIM_OUTPUT); } } -void USTelemedScanConverterPlugin::SetOutputImage(mitk::Image::Pointer outputImage, itk::FastMutexLock::Pointer outputImageMutex) +void USTelemedScanConverterPlugin::SetOutputImage(mitk::Image::Pointer outputImage, std::mutex* outputImageMutex) { m_OutputImage = outputImage; m_OutputImageMutex = outputImageMutex; } STDMETHODIMP USTelemedScanConverterPlugin::SetScanConverterPlugin(IDispatch* plugin) { // make sure that there is no scan converter plugin registered already this->ReleasePlugin(); HRESULT hr; // it is ok to call this method with a nullptr plugin to remove // a previous callback if (plugin == nullptr) { MITK_INFO("IUsgfwScanConverterPluginCB")("ScanConverterPlugin") << "nullptr plugin set to the scan converter. The callback for the previous plugin is removed now."; return S_OK; } // get Telemed API plugin from the COM library Usgfw2Lib::IUsgScanConverterPlugin* tmp_plugin; hr = plugin->QueryInterface(__uuidof(Usgfw2Lib::IUsgScanConverterPlugin), (void**)&tmp_plugin); if (FAILED(hr)) { MITK_WARN("IUsgfwScanConverterPluginCB")("ScanConverterPlugin") << "Could not query com interface for IUsgScanConverterPlugin (" << hr << ")."; return hr; } // get the converter for scan lines from the COM library and // save it as a member attribute hr = tmp_plugin->get_ScanConverter((IUnknown**)&m_Plugin); if (FAILED(hr)) { MITK_WARN("IUsgfwScanConverterPluginCB")("ScanConverterPlugin") << "Could not get ScanConverter from plugin (" << hr << ")."; return hr; } SAFE_RELEASE(tmp_plugin); // now the callback can be set -> interface functions of this // object will be called from now on when new image data is // available hr = m_Plugin->SetCallback(this,USPC_BUFFER_INTERIM_OUTPUT); if (FAILED(hr)) { MITK_WARN("IUsgfwScanConverterPluginCB")("ScanConverterPlugin") << "Could not set callback for plugin (" << hr << ")."; return hr; } return S_OK; } diff --git a/Modules/US/USHardwareTelemed/mitkUSTelemedScanConverterPlugin.h b/Modules/US/USHardwareTelemed/mitkUSTelemedScanConverterPlugin.h index c4ec8e8235..2ad357451d 100644 --- a/Modules/US/USHardwareTelemed/mitkUSTelemedScanConverterPlugin.h +++ b/Modules/US/USHardwareTelemed/mitkUSTelemedScanConverterPlugin.h @@ -1,166 +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 MITKUSTelemedScanConverterPlugin_H_HEADER_INCLUDED_ #define MITKUSTelemedScanConverterPlugin_H_HEADER_INCLUDED_ #include #include #include #include #include +#include #include "mitkUSTelemedSDKHeader.h" #include "mitkImage.h" -#include "itkFastMutexLock.h" + /** * \brief Telemed API plugin for getting images from scan lines. * Implements a COM interface whereat only the function InterimOutBufferCB * is used for copying given image buffer into a mitk::Image. * * A pointer to this mitk::Image must be set by calling * mitk::USTelemedScanConverterPlugin::SetOutputImage() first. * The image content is then updated every time the Telemed API calls * the implemented callback function of this class. * * For more infomration about the implemented COM interface refer to the * Telemed API documentation. */ class USTelemedScanConverterPlugin : public IUsgfwScanConverterPluginCB { public: USTelemedScanConverterPlugin( ); ~USTelemedScanConverterPlugin( ); // internal functions for Telemed API virtual HRESULT __stdcall QueryInterface(const IID& iid,void** ppv); virtual ULONG __stdcall AddRef(); virtual ULONG __stdcall Release(); /** * Setter for a pointer to a mitk::Image in which the current * image buffer from the Telemed API will be stored at every * API callback. This function must be called before image data * can be got from this class. * A pointer to a mutex can be set in addition. This mutex will * be locked on every writing to the given image. */ - void SetOutputImage(mitk::Image::Pointer outputImage, itk::FastMutexLock::Pointer outputImageMutex = 0); + void SetOutputImage(mitk::Image::Pointer outputImage, std::mutex* outputImageMutex); // receives pointers to input and output media samples STDMETHOD(SampleCB) ( IMediaSample *pSampleIn, IMediaSample *pSampleOut, int nOutX1, int nOutY1, int nOutX2, int nOutY2 ) {return S_OK;} // receives pointers to input and output sample buffers STDMETHOD(BufferCB) ( PBYTE pBufferIn, int nInBufferLen, PBYTE pBufferOut, int nOutBufferLen, int nOutX1, int nOutY1, int nOutX2, int nOutY2 ) {return S_OK;} // receives pointers to input and intermediate sample buffers STDMETHOD(InInterimBufferCB) ( PBYTE pBufferIn, int nInBufferLen, PBYTE pBufferInterim, int nInterimBufferLen, int nOutX1, int nOutY1, int nOutX2, int nOutY2 ) {return S_OK;} // receves pointers to input media sample and intermediatesample buffer STDMETHOD(InInterimSampleCB) ( IMediaSample *pSampleIn, PBYTE pBufferInterim, int nInterimBufferLen, int nOutX1, int nOutY1, int nOutX2, int nOutY2 ) {return S_OK;} // receives pointers to output and intermediate sample buffers STDMETHOD(InterimOutBufferCB) ( PBYTE pBufferInterim, int nInterimBufferLen, PBYTE pBufferOut, int nOutBufferLen, int nOutX1, int nOutY1, int nOutX2, int nOutY2 ); // receives pointers to output media sample and intermediate sample buffer STDMETHOD(InterimOutSampleCB) ( PBYTE pBufferInterim, int nInterimBufferLen, IMediaSample *pSampleIn, int nOutX1, int nOutY1, int nOutX2, int nOutY2 ) {return S_OK;} // receives conversion parameter change pin index // if parameter is negative parameter was changed by some filter interface STDMETHOD(ParameterCB) ( int nPin ) { return S_OK; } STDMETHOD(SetScanConverterPlugin)(IDispatch* plugin); //STDMETHOD(getSource)(LONG* plugin); protected: /** * Remove Telemed API callback and release and delete m_Plugin attribute. */ void ReleasePlugin( ); /** * Telemed API object for handling callbacks on new image data. */ IUsgfwScanConverterPlugin* m_Plugin; /** * Pointer to mitk::Image in which the current image buffer * from the Telemed API will be stored at every API callback. */ mitk::Image::Pointer m_OutputImage; /** * Mutex for the output image. Has to be set together with the * output image via SetOutputImage(). */ - itk::FastMutexLock::Pointer m_OutputImageMutex; + std::mutex* m_OutputImageMutex; private: long m_cRef ; }; #endif // MITKUSTelemedScanConverterPlugin_H_HEADER_INCLUDED_ diff --git a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpSearchView.cpp b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpSearchView.cpp index 4bba2eff07..5703479177 100644 --- a/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpSearchView.cpp +++ b/Plugins/org.blueberry.ui.qt.help/src/internal/berryHelpSearchView.cpp @@ -1,232 +1,233 @@ /*============================================================================ 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 "berryHelpSearchView.h" #include "berryHelpPluginActivator.h" #include "berryQHelpEngineWrapper.h" #include "berryHelpEditorInput.h" #include "berryHelpEditor.h" #include #include #include #include #include #include #include #include #include #include namespace berry { HelpSearchView::HelpSearchView() : m_ZoomCount(0) , m_Parent(nullptr) , m_SearchEngine(HelpPluginActivator::getInstance()->getQHelpEngine().searchEngine()) , m_ResultWidget(nullptr) , m_QueryWidget(nullptr) { } HelpSearchView::~HelpSearchView() { // prevent deletion of the widget m_ResultWidget->setParent(nullptr); } void HelpSearchView::CreateQtPartControl(QWidget* parent) { if (m_ResultWidget == nullptr) { m_Parent = parent; auto vLayout = new QVBoxLayout(parent); // This will be lead to strange behavior when using multiple instances of this view // because the QHelpSearchResultWidget instance is shared. The new view will // reparent the widget. m_ResultWidget = m_SearchEngine->resultWidget(); m_QueryWidget = new QHelpSearchQueryWidget(); vLayout->addWidget(m_QueryWidget); vLayout->addWidget(m_ResultWidget); connect(m_QueryWidget, SIGNAL(search()), this, SLOT(search())); connect(m_ResultWidget, SIGNAL(requestShowLink(QUrl)), this, SLOT(requestShowLink(QUrl))); connect(m_SearchEngine, SIGNAL(searchingStarted()), this, SLOT(searchingStarted())); connect(m_SearchEngine, SIGNAL(searchingFinished(int)), this, SLOT(searchingFinished(int))); QTextBrowser* browser = m_ResultWidget->findChild(); if (browser) // Will be null if QtHelp was configured not to use CLucene. { + browser->document()->setDefaultStyleSheet(QStringLiteral("body { background-color: white; }")); browser->viewport()->installEventFilter(this); browser->setContextMenuPolicy(Qt::CustomContextMenu); connect(browser, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint))); } } } void HelpSearchView::SetFocus() { if (!(m_ResultWidget->hasFocus())) { m_QueryWidget->setFocus(); } } void HelpSearchView::zoomIn() { QTextBrowser* browser = m_ResultWidget->findChild(); if (browser && m_ZoomCount != 10) { m_ZoomCount++; browser->zoomIn(); } } void HelpSearchView::zoomOut() { QTextBrowser* browser = m_ResultWidget->findChild(); if (browser && m_ZoomCount != -5) { m_ZoomCount--; browser->zoomOut(); } } void HelpSearchView::resetZoom() { if (m_ZoomCount == 0) return; QTextBrowser* browser = m_ResultWidget->findChild(); if (browser) { browser->zoomOut(m_ZoomCount); m_ZoomCount = 0; } } void HelpSearchView::search() const { QList query = m_QueryWidget->query(); m_SearchEngine->search(query); } void HelpSearchView::searchingStarted() { m_Parent->setCursor(QCursor(Qt::WaitCursor)); } void HelpSearchView::searchingFinished(int hits) { Q_UNUSED(hits) m_Parent->unsetCursor(); //qApp->restoreOverrideCursor(); } void HelpSearchView::requestShowLink(const QUrl &link) { HelpPluginActivator::linkActivated(this->GetSite()->GetPage(), link); } bool HelpSearchView::eventFilter(QObject* o, QEvent *e) { QTextBrowser* browser = m_ResultWidget->findChild(); if (browser && o == browser->viewport() && e->type() == QEvent::MouseButtonRelease) { QMouseEvent* me = static_cast(e); QUrl link = m_ResultWidget->linkAt(me->pos()); if (!link.isEmpty() || link.isValid()) { bool controlPressed = me->modifiers() & Qt::ControlModifier; if((me->button() == Qt::LeftButton && controlPressed) || (me->button() == Qt::MidButton)) { IEditorInput::Pointer input(new HelpEditorInput(link)); this->GetSite()->GetPage()->OpenEditor(input, HelpEditor::EDITOR_ID); } } } return QObject::eventFilter(o,e); } void HelpSearchView::showContextMenu(const QPoint& point) { QMenu menu; QTextBrowser* browser = m_ResultWidget->findChild(); if (!browser) return; // QPoint point = browser->mapFromGlobal(pos); // if (!browser->rect().contains(point, true)) // return; QUrl link = browser->anchorAt(point); QKeySequence keySeq(QKeySequence::Copy); QAction *copyAction = menu.addAction(tr("&Copy") + QLatin1String("\t") + keySeq.toString(QKeySequence::NativeText)); copyAction->setEnabled(QTextCursor(browser->textCursor()).hasSelection()); QAction *copyAnchorAction = menu.addAction(tr("Copy &Link Location")); copyAnchorAction->setEnabled(!link.isEmpty() && link.isValid()); keySeq = QKeySequence(Qt::CTRL); QAction *newTabAction = menu.addAction(tr("Open Link in New Tab") + QLatin1String("\t") + keySeq.toString(QKeySequence::NativeText) + QLatin1String("LMB")); newTabAction->setEnabled(!link.isEmpty() && link.isValid()); menu.addSeparator(); keySeq = QKeySequence::SelectAll; QAction *selectAllAction = menu.addAction(tr("Select All") + QLatin1String("\t") + keySeq.toString(QKeySequence::NativeText)); QAction *usedAction = menu.exec(browser->mapToGlobal(point)); if (usedAction == copyAction) { QTextCursor cursor = browser->textCursor(); if (!cursor.isNull() && cursor.hasSelection()) { QString selectedText = cursor.selectedText(); auto data = new QMimeData(); data->setText(selectedText); QApplication::clipboard()->setMimeData(data); } } else if (usedAction == copyAnchorAction) { QApplication::clipboard()->setText(link.toString()); } else if (usedAction == newTabAction) { IEditorInput::Pointer input(new HelpEditorInput(link)); this->GetSite()->GetPage()->OpenEditor(input, HelpEditor::EDITOR_ID); } else if (usedAction == selectAllAction) { browser->selectAll(); } } } diff --git a/Plugins/org.mitk.gui.qt.flow.segmentation/manifest_headers.cmake b/Plugins/org.mitk.gui.qt.flow.segmentation/manifest_headers.cmake index 724db97aef..996ab595c6 100644 --- a/Plugins/org.mitk.gui.qt.flow.segmentation/manifest_headers.cmake +++ b/Plugins/org.mitk.gui.qt.flow.segmentation/manifest_headers.cmake @@ -1,5 +1,5 @@ set(Plugin-Name "Flow Segmentation Control") set(Plugin-Version "0.1") set(Plugin-Vendor "DKFZ, Medical Image Computing") set(Plugin-ContactAddress "http://www.mitk.org") -set(Require-Plugin org.mitk.gui.qt.common org.mitk.gui.qt.multilabelsegmentation org.mitk.gui.qt.flowapplication) +set(Require-Plugin org.mitk.gui.qt.common org.mitk.gui.qt.segmentation org.mitk.gui.qt.flowapplication) diff --git a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/perspectives/QmitkFlowSegmentationPerspective.cpp b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/perspectives/QmitkFlowSegmentationPerspective.cpp index 6cdfb38ae0..cb09bd4f9f 100644 --- a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/perspectives/QmitkFlowSegmentationPerspective.cpp +++ b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/perspectives/QmitkFlowSegmentationPerspective.cpp @@ -1,43 +1,43 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkFlowSegmentationPerspective.h" #include "berryIViewLayout.h" QmitkFlowSegmentationPerspective::QmitkFlowSegmentationPerspective() { } void QmitkFlowSegmentationPerspective::CreateInitialLayout(berry::IPageLayout::Pointer layout) { QString editorArea = layout->GetEditorArea(); - layout->AddView("org.mitk.views.multilabelsegmentation", berry::IPageLayout::LEFT, 0.3f, editorArea); + layout->AddView("org.mitk.views.segmentation", berry::IPageLayout::LEFT, 0.3f, editorArea); - berry::IViewLayout::Pointer lo = layout->GetViewLayout("org.mitk.views.multilabelsegmentation"); + berry::IViewLayout::Pointer lo = layout->GetViewLayout("org.mitk.views.segmentation"); lo->SetCloseable(false); layout->AddStandaloneView("org.mitk.views.flow.control",false, berry::IPageLayout::RIGHT, 0.6f, editorArea); lo = layout->GetViewLayout("org.mitk.views.flow.control"); lo->SetCloseable(false); layout->AddView("org.mitk.views.imagenavigator", berry::IPageLayout::TOP, 0.1f, "org.mitk.views.flow.control"); berry::IPlaceholderFolderLayout::Pointer bottomFolder = layout->CreatePlaceholderFolder("bottom", berry::IPageLayout::BOTTOM, 0.7f, editorArea); bottomFolder->AddPlaceholder("org.blueberry.views.logview"); berry::IPlaceholderFolderLayout::Pointer rightFolder = layout->CreatePlaceholderFolder("right", berry::IPageLayout::RIGHT, 0.3f, editorArea); rightFolder->AddPlaceholder("org.mitk.views.datamanager"); layout->AddPerspectiveShortcut("org.mitk.qt.flowapplication.defaultperspective"); } diff --git a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUltrasoundCalibration.cpp b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUltrasoundCalibration.cpp index 01c5869c50..be23087d0c 100644 --- a/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUltrasoundCalibration.cpp +++ b/Plugins/org.mitk.gui.qt.igt.app.ultrasoundtrackingnavigation/src/internal/QmitkUltrasoundCalibration.cpp @@ -1,1397 +1,1398 @@ /*============================================================================ 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. ============================================================================*/ // Blueberry #include #include // Qmitk #include "QmitkUltrasoundCalibration.h" #include // Qt #include #include #include #include // MITK #include #include "mitkIOUtil.h" #include #include #include #include #include #include #include #include #include // us #include // VTK #include #include #include #include #include #include #include #include "internal/org_mbi_gui_qt_usnavigation_Activator.h" // sleep headers #include #include const std::string QmitkUltrasoundCalibration::VIEW_ID = "org.mitk.views.ultrasoundcalibration"; QmitkUltrasoundCalibration::QmitkUltrasoundCalibration() : m_PhantomConfigurationPointSet(nullptr), m_USDeviceChanged(this, &QmitkUltrasoundCalibration::OnUSDepthChanged) { ctkPluginContext *pluginContext = mitk::PluginActivator::GetContext(); if (pluginContext) { // to be notified about service event of an USDevice pluginContext->connectServiceListener(this, "OnDeviceServiceEvent", QString::fromStdString("(" + us::ServiceConstants::OBJECTCLASS() + "=" + us_service_interface_iid() + ")")); } } QmitkUltrasoundCalibration::~QmitkUltrasoundCalibration() { m_Controls.m_CombinedModalityManagerWidget->blockSignals(true); mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality; combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (combinedModality.IsNotNull()) { combinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_USDeviceChanged); } m_Timer->stop(); this->OnStopCalibrationProcess(); this->OnStopPlusCalibration(); mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path"); if (node.IsNotNull()) this->GetDataStorage()->Remove(node); this->GetDataStorage()->Remove(m_VerificationReferencePointsDataNode); - + this->GetDataStorage()->Remove(m_SpacingNode); + delete m_Timer; // remove observer for phantom-based point adding m_CalibPointsImage->RemoveAllObservers(); } void QmitkUltrasoundCalibration::SetFocus() { m_Controls.m_ToolBox->setFocus(); } void QmitkUltrasoundCalibration::CreateQtPartControl(QWidget *parent) { // create GUI widgets from the Qt Designer's .ui file m_Controls.setupUi(parent); m_Controls.m_CombinedModalityManagerWidget->SetCalibrationLoadedNecessary(false); m_Timer = new QTimer(this); m_StreamingTimer = new QTimer(this); m_Controls.m_SpacingBtnFreeze->setEnabled(true); m_Controls.m_SpacingAddPoint->setEnabled(false); m_Controls.m_CalculateSpacing->setEnabled(false); m_SpacingPointsCount = 0; m_SpacingPoints = mitk::PointSet::New(); m_SpacingNode = mitk::DataNode::New(); m_SpacingNode->SetName("Spacing Points"); m_SpacingNode->SetData(this->m_SpacingPoints); this->GetDataStorage()->Add(m_SpacingNode); // Pointset for Calibration Points m_CalibPointsTool = mitk::PointSet::New(); // Pointset for Worldpoints m_CalibPointsImage = mitk::PointSet::New(); m_CalibPointsCount = 0; // Evaluation Pointsets (Non-Visualized) m_EvalPointsImage = mitk::PointSet::New(); m_EvalPointsTool = mitk::PointSet::New(); m_EvalPointsProjected = mitk::PointSet::New(); // Neelde Projection Filter m_NeedleProjectionFilter = mitk::NeedleProjectionFilter::New(); // Tracking Status Widgets m_Controls.m_CalibTrackingStatus->ShowStatusLabels(); m_Controls.m_EvalTrackingStatus->ShowStatusLabels(); // General & Device Selection connect(m_Timer, SIGNAL(timeout()), this, SLOT(Update())); // Calibration connect(m_Controls.m_CalibBtnFreeze, SIGNAL(clicked()), this, SLOT(SwitchFreeze())); // Freeze connect(m_Controls.m_CalibBtnAddPoint, SIGNAL(clicked()), this, SLOT(OnAddCalibPoint())); // Tracking & Image Points (Calibration) connect(m_Controls.m_CalibBtnCalibrate, SIGNAL(clicked()), this, SLOT(OnCalibration())); // Perform Calibration // Phantom-based calibration connect(m_Controls.m_CalibBtnLoadPhantomConfiguration, SIGNAL(clicked()), this, SLOT(OnLoadPhantomConfiguration())); // Phantom configuration connect(m_Controls.m_CalibBtnMatchAnnotationToPhantomConfiguration, SIGNAL(clicked()), this, SLOT(OnMatchAnnotationToPhantomConfiguration())); connect(m_Controls.m_CalibBtnMoveUp, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsUp())); connect(m_Controls.m_CalibBtnMoveDown, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsDown())); connect(m_Controls.m_CalibBtnMoveLeft, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsLeft())); connect(m_Controls.m_CalibBtnMoveRight, SIGNAL(clicked()), this, SLOT(OnMovePhantomAnnotationsRight())); connect(m_Controls.m_CalibBtnRotateRight, SIGNAL(clicked()), this, SLOT(OnRotatePhantomAnnotationsRight())); connect(m_Controls.m_CalibBtnRotateLeft, SIGNAL(clicked()), this, SLOT(OnRotatePhantomAnnotationsLeft())); connect(m_Controls.m_CalibBtnPerformPhantomCalibration, SIGNAL(clicked()), this, SLOT(OnPhantomBasedCalibration())); // Perform phantom-based calibration connect(m_Controls.m_CalibBtnSavePhantomCalibration, SIGNAL(clicked()), this, SLOT(OnSaveCalibration())); // Save phantom-based calibration // Evaluation connect(m_Controls.m_EvalBtnStep1, SIGNAL(clicked()), this, SLOT(OnAddEvalProjectedPoint())); // Needle Projection connect(m_Controls.m_EvalBtnStep2, SIGNAL(clicked()), this, SLOT(SwitchFreeze())); // Freeze connect(m_Controls.m_EvalBtnStep3, SIGNAL(clicked()), this, SLOT(OnAddEvalTargetPoint())); // Tracking & Image Points (Evaluation) connect(m_Controls.m_EvalBtnSave, SIGNAL(clicked()), this, SLOT(OnSaveEvaluation())); // Save Evaluation Results connect(m_Controls.m_CalibBtnSaveCalibration, SIGNAL(clicked()), this, SLOT(OnSaveCalibration())); // Save Evaluation Results connect(m_Controls.m_BtnReset, SIGNAL(clicked()), this, SLOT(OnReset())); // Reset Pointsets // PLUS Calibration connect(m_Controls.m_GetCalibrationFromPLUS, SIGNAL(clicked()), this, SLOT(OnGetPlusCalibration())); connect(m_Controls.m_StartStreaming, SIGNAL(clicked()), this, SLOT(OnStartStreaming())); connect(m_StreamingTimer, SIGNAL(timeout()), this, SLOT(OnStreamingTimerTimeout())); connect(m_Controls.m_StopPlusCalibration, SIGNAL(clicked()), this, SLOT(OnStopPlusCalibration())); connect(m_Controls.m_SavePlusCalibration, SIGNAL(clicked()), this, SLOT(OnSaveCalibration())); connect(this, SIGNAL(NewConnectionSignal()), this, SLOT(OnNewConnection())); // Determine Spacing for Calibration of USVideoDevice connect(m_Controls.m_SpacingBtnFreeze, SIGNAL(clicked()), this, SLOT(OnFreezeClicked())); connect(m_Controls.m_SpacingAddPoint, SIGNAL(clicked()), this, SLOT(OnAddSpacingPoint())); connect(m_Controls.m_CalculateSpacing, SIGNAL(clicked()), this, SLOT(OnCalculateSpacing())); connect(m_Controls.m_CombinedModalityManagerWidget, SIGNAL(SignalReadyForNextStep()), this, SLOT(OnDeviceSelected())); connect(m_Controls.m_CombinedModalityManagerWidget, SIGNAL(SignalNoLongerReadyForNextStep()), this, SLOT(OnDeviceDeselected())); connect(m_Controls.m_StartCalibrationButton, SIGNAL(clicked()), this, SLOT(OnStartCalibrationProcess())); connect(m_Controls.m_StartPlusCalibrationButton, SIGNAL(clicked()), this, SLOT(OnStartPlusCalibration())); connect(m_Controls.m_CalibBtnRestartCalibration, SIGNAL(clicked()), this, SLOT(OnReset())); connect(m_Controls.m_CalibBtnStopCalibration, SIGNAL(clicked()), this, SLOT(OnStopCalibrationProcess())); connect(m_Controls.m_AddReferencePoints, SIGNAL(clicked()), this, SLOT(OnAddCurrentTipPositionToReferencePoints())); connect(m_Controls.m_AddCurrentPointerTipForVerification, SIGNAL(clicked()), this, SLOT(OnAddCurrentTipPositionForVerification())); connect(m_Controls.m_StartVerification, SIGNAL(clicked()), this, SLOT(OnStartVerification())); // initialize data storage combo box m_Controls.m_ReferencePointsComboBox->SetDataStorage(this->GetDataStorage()); m_Controls.m_ReferencePointsComboBox->SetAutoSelectNewItems(true); m_Controls.m_ReferencePointsComboBox->SetPredicate(mitk::NodePredicateDataType::New("PointSet")); // initialize point list widget if (m_VerificationReferencePoints.IsNull()) { m_VerificationReferencePoints = mitk::PointSet::New(); } if (m_VerificationReferencePointsDataNode.IsNull()) { m_VerificationReferencePointsDataNode = mitk::DataNode::New(); m_VerificationReferencePointsDataNode->SetName("US Verification Reference Points"); m_VerificationReferencePointsDataNode->SetData(m_VerificationReferencePoints); this->GetDataStorage()->Add(m_VerificationReferencePointsDataNode); } m_Controls.m_ReferencePointsPointListWidget->SetPointSetNode(m_VerificationReferencePointsDataNode); m_Controls.m_ToolBox->setCurrentIndex(0); } void QmitkUltrasoundCalibration::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*source*/, const QList & /*nodes*/) { } void QmitkUltrasoundCalibration::OnTabSwitch(int index) { switch (index) { case 0: if (m_Controls.m_ToolBox->isItemEnabled(1) || m_Controls.m_ToolBox->isItemEnabled(2)) { this->OnStopCalibrationProcess(); } break; default:; } } void QmitkUltrasoundCalibration::OnDeviceSelected() { mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality; combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (combinedModality.IsNotNull()) { combinedModality->GetUltrasoundDevice()->AddPropertyChangedListener(m_USDeviceChanged); m_Controls.m_StartCalibrationButton->setEnabled(true); m_Controls.m_StartPlusCalibrationButton->setEnabled(true); m_Controls.m_ToolBox->setItemEnabled(1, true); m_Controls.m_ToolBox->setItemEnabled(2, true); } } void QmitkUltrasoundCalibration::OnDeviceDeselected() { mitk::AbstractUltrasoundTrackerDevice::Pointer combinedModality; combinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (combinedModality.IsNotNull()) { combinedModality->GetUltrasoundDevice()->RemovePropertyChangedListener(m_USDeviceChanged); } m_Controls.m_StartCalibrationButton->setEnabled(false); m_Controls.m_StartPlusCalibrationButton->setEnabled(false); m_Controls.m_ToolBox->setCurrentIndex(0); m_Controls.m_ToolBox->setItemEnabled(1, false); m_Controls.m_ToolBox->setItemEnabled(2, false); } void QmitkUltrasoundCalibration::OnAddCurrentTipPositionToReferencePoints() { if (m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource().IsNull() || (m_Controls.m_VerificationPointerChoser->GetSelectedToolID() == -1)) { MITK_WARN << "No tool selected, aborting"; return; } mitk::NavigationData::Pointer currentPointerData = m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource()->GetOutput( m_Controls.m_VerificationPointerChoser->GetSelectedToolID()); mitk::Point3D currentTipPosition = currentPointerData->GetPosition(); m_VerificationReferencePoints->InsertPoint(m_VerificationReferencePoints->GetSize(), currentTipPosition); } void QmitkUltrasoundCalibration::OnStartVerification() { m_currentPoint = 0; mitk::PointSet::Pointer selectedPointSet = dynamic_cast(m_Controls.m_ReferencePointsComboBox->GetSelectedNode()->GetData()); m_Controls.m_CurrentPointLabel->setText("Point " + QString::number(m_currentPoint) + " of " + QString::number(selectedPointSet->GetSize())); m_allErrors = std::vector(); m_allReferencePoints = std::vector(); for (int i = 0; i < selectedPointSet->GetSize(); i++) { m_allReferencePoints.push_back(selectedPointSet->GetPoint(i)); } } void QmitkUltrasoundCalibration::OnAddCurrentTipPositionForVerification() { if (m_currentPoint == -1) { MITK_WARN << "Cannot add point"; return; } if (m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource().IsNull() || (m_Controls.m_VerificationPointerChoser->GetSelectedToolID() == -1)) { MITK_WARN << "No tool selected, aborting"; return; } mitk::NavigationData::Pointer currentPointerData = m_Controls.m_VerificationPointerChoser->GetSelectedNavigationDataSource()->GetOutput( m_Controls.m_VerificationPointerChoser->GetSelectedToolID()); mitk::Point3D currentTipPosition = currentPointerData->GetPosition(); double currentError = m_allReferencePoints.at(m_currentPoint).EuclideanDistanceTo(currentTipPosition); MITK_INFO << "Current Error: " << currentError << " mm"; m_allErrors.push_back(currentError); if (++m_currentPoint < static_cast(m_allReferencePoints.size())) { m_Controls.m_CurrentPointLabel->setText("Point " + QString::number(m_currentPoint) + " of " + QString::number(m_allReferencePoints.size())); } else { m_currentPoint = -1; double meanError = 0; for (std::size_t i = 0; i < m_allErrors.size(); ++i) { meanError += m_allErrors[i]; } meanError /= m_allErrors.size(); QString result = "Finished verification! \n Verification of " + QString::number(m_allErrors.size()) + " points, mean error: " + QString::number(meanError) + " mm"; m_Controls.m_ResultsTextEdit->setText(result); MITK_INFO << result.toStdString(); } } void QmitkUltrasoundCalibration::OnStartCalibrationProcess() { // US Image Stream m_Node = dynamic_cast(this->GetDataStorage()->GetNamedNode("US Viewing Stream - Image 0")->CreateAnother().GetPointer()); m_Node->SetName("US Calibration Viewing Stream"); this->GetDataStorage()->Add(m_Node); // data node for calibration point set m_CalibNode = mitk::DataNode::New(); m_CalibNode->SetName("Tool Calibration Points"); m_CalibNode->SetData(this->m_CalibPointsTool); this->GetDataStorage()->Add(m_CalibNode); // data node for world point set m_WorldNode = mitk::DataNode::New(); m_WorldNode->SetName("Image Calibration Points"); m_WorldNode->SetData(this->m_CalibPointsImage); this->GetDataStorage()->Add(m_WorldNode); m_CombinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); m_CombinedModality->SetCalibration(mitk::AffineTransform3D::New()); // dummy calibration because without a calibration // the comined modality was laggy (maybe a bug?) if (m_CombinedModality.IsNull()) { return; } m_Tracker = m_CombinedModality->GetNavigationDataSource(); // Construct Pipeline this->m_NeedleProjectionFilter->SetInput(0, m_Tracker->GetOutput(0)); QApplication::setOverrideCursor(Qt::WaitCursor); // make sure that the combined modality is in connected state before using it if (m_CombinedModality->GetUltrasoundDevice()->GetDeviceState() < mitk::USDevice::State_Connected) { m_CombinedModality->GetUltrasoundDevice()->Connect(); } if (m_CombinedModality->GetUltrasoundDevice()->GetDeviceState() < mitk::USDevice::State_Activated) { m_CombinedModality->GetUltrasoundDevice()->Activate(); } QApplication::restoreOverrideCursor(); this->SwitchFreeze(); // Trigger the ProbeChanged method for initializing/updating the spacing of the ultrasound image correctly std::string probeName = m_CombinedModality->GetUltrasoundDevice()->GetCurrentProbe()->GetName(); m_CombinedModality->GetUltrasoundDevice()->ProbeChanged(probeName); mitk::DataNode::Pointer usNode = this->GetDataStorage()->GetNamedNode("US Viewing Stream - Image 0"); if (usNode.IsNotNull()) { this->GetDataStorage()->Remove(usNode); } // Todo: Maybe display this elsewhere this->ShowNeedlePath(); // Switch active tab to Calibration page m_Controls.m_ToolBox->setItemEnabled(1, true); m_Controls.m_ToolBox->setCurrentIndex(1); } void QmitkUltrasoundCalibration::OnStartPlusCalibration() { if (m_CombinedModality.IsNull()) { m_CombinedModality = m_Controls.m_CombinedModalityManagerWidget->GetSelectedCombinedModality(); if (m_CombinedModality.IsNull()) { return; } // something went wrong, there is no combined modality } // setup server to send UltrasoundImages to PLUS mitk::IGTLServer::Pointer m_USServer = mitk::IGTLServer::New(true); m_USServer->SetName("EchoTrack Image Source"); m_USServer->SetHostname("127.0.0.1"); m_USServer->SetPortNumber(18944); m_USMessageProvider = mitk::IGTLMessageProvider::New(); m_USMessageProvider->SetIGTLDevice(m_USServer); m_USMessageProvider->SetFPS(5); m_USImageToIGTLMessageFilter = mitk::ImageToIGTLMessageFilter::New(); m_USImageToIGTLMessageFilter->ConnectTo(m_CombinedModality->GetUltrasoundDevice()); m_USImageToIGTLMessageFilter->SetName("USImage Filter"); // setup server to send TrackingData to PLUS m_TrackingServer = mitk::IGTLServer::New(true); m_TrackingServer->SetName("EchoTrack Tracking Source"); m_TrackingServer->SetHostname("127.0.0.1"); m_TrackingServer->SetPortNumber(18945); m_TrackingMessageProvider = mitk::IGTLMessageProvider::New(); m_TrackingMessageProvider->SetIGTLDevice(m_TrackingServer); m_TrackingMessageProvider->SetFPS(5); m_TrackingToIGTLMessageFilter = mitk::NavigationDataToIGTLMessageFilter::New(); m_TrackingToIGTLMessageFilter->ConnectTo(m_CombinedModality->GetTrackingDeviceDataSource()); m_TrackingToIGTLMessageFilter->SetName("Tracker Filter"); typedef itk::SimpleMemberCommand CurCommandType; CurCommandType::Pointer newConnectionCommand = CurCommandType::New(); newConnectionCommand->SetCallbackFunction(this, &QmitkUltrasoundCalibration::OnPlusConnected); this->m_NewConnectionObserverTag = this->m_TrackingServer->AddObserver(mitk::NewClientConnectionEvent(), newConnectionCommand); // Open connections of both servers if (m_USServer->OpenConnection()) { MITK_INFO << "US Server opened its connection successfully"; m_USServer->StartCommunication(); } else { MITK_INFO << "US Server could not open its connection"; } if (m_TrackingServer->OpenConnection()) { MITK_INFO << "Tracking Server opened its connection successfully"; m_TrackingServer->StartCommunication(); } else { MITK_INFO << "Tracking Server could not open its connection"; } if (m_USMessageProvider->IsCommunicating() && m_TrackingMessageProvider->IsCommunicating()) { m_Controls.m_StartPlusCalibrationButton->setEnabled(false); m_Controls.m_GetCalibrationFromPLUS->setEnabled(true); m_Controls.m_StartStreaming->setEnabled(false); m_Controls.m_SavePlusCalibration->setEnabled(false); m_Controls.m_SetupStatus->setStyleSheet("QLabel { color : green; }"); m_Controls.m_SetupStatus->setText("Setup successfull you can now connect PLUS"); } else { m_Controls.m_SetupStatus->setStyleSheet("QLabel { color : red; }"); m_Controls.m_SetupStatus->setText("Something went wrong. Please try again"); } } void QmitkUltrasoundCalibration::OnStopPlusCalibration() { // closing all server and clients when PlusCalibration is finished if (m_USMessageProvider.IsNotNull()) { if (m_USMessageProvider->IsStreaming()) { m_USMessageProvider->StopStreamingOfSource(m_USImageToIGTLMessageFilter); } } if (m_TrackingMessageProvider.IsNotNull()) { if (m_TrackingMessageProvider->IsStreaming()) { m_TrackingMessageProvider->StopStreamingOfSource(m_TrackingToIGTLMessageFilter); } } if (m_USServer.IsNotNull()) { m_USServer->CloseConnection(); } if (m_TrackingServer.IsNotNull()) { m_TrackingServer->CloseConnection(); } if (m_TransformClient.IsNotNull()) { m_TransformClient->CloseConnection(); } m_Controls.m_GotCalibrationLabel->setText(""); m_Controls.m_ConnectionStatus->setText(""); m_Controls.m_SetupStatus->setText(""); m_Controls.m_StartPlusCalibrationButton->setEnabled(true); m_StreamingTimer->stop(); delete m_StreamingTimer; } void QmitkUltrasoundCalibration::OnPlusConnected() { emit NewConnectionSignal(); } void QmitkUltrasoundCalibration::OnNewConnection() { m_Controls.m_StartStreaming->setEnabled(true); m_Controls.m_ConnectionStatus->setStyleSheet("QLabel { color : green; }"); m_Controls.m_ConnectionStatus->setText("Connection successfull you can now start streaming"); } void QmitkUltrasoundCalibration::OnStreamingTimerTimeout() { m_USMessageProvider->Update(); m_TrackingMessageProvider->Update(); } void QmitkUltrasoundCalibration::OnStartStreaming() { m_USMessageProvider->StartStreamingOfSource(m_USImageToIGTLMessageFilter, 5); m_TrackingMessageProvider->StartStreamingOfSource(m_TrackingToIGTLMessageFilter, 5); m_Controls.m_StartStreaming->setEnabled(false); m_Controls.m_ConnectionStatus->setText(""); m_StreamingTimer->start((1.0 / 5.0 * 1000.0)); } void QmitkUltrasoundCalibration::OnGetPlusCalibration() { m_TransformClient = mitk::IGTLClient::New(true); m_TransformClient->SetHostname("127.0.0.1"); m_TransformClient->SetPortNumber(18946); m_TransformDeviceSource = mitk::IGTLDeviceSource::New(); m_TransformDeviceSource->SetIGTLDevice(m_TransformClient); m_TransformDeviceSource->Connect(); if (m_TransformDeviceSource->IsConnected()) { MITK_INFO << "successfully connected"; m_TransformDeviceSource->StartCommunication(); if (m_TransformDeviceSource->IsCommunicating()) { MITK_INFO << "communication started"; mitk::IGTLMessage::Pointer receivedMessage; bool condition = false; igtl::Matrix4x4 transformPLUS; while (!(receivedMessage.IsNotNull() && receivedMessage->IsDataValid())) { std::this_thread::sleep_for(std::chrono::milliseconds(50)); m_TransformDeviceSource->Update(); receivedMessage = m_TransformDeviceSource->GetOutput(); igtl::TransformMessage::Pointer msg = dynamic_cast(m_TransformDeviceSource->GetOutput()->GetMessage().GetPointer()); if (msg == nullptr || msg.IsNull()) { MITK_INFO << "Received message could not be casted to TransformMessage. Skipping..."; continue; } else { if (std::strcmp(msg->GetDeviceName(), "ImageToTracker") != 0) { MITK_INFO << "Was not Image to Tracker Transform. Skipping..."; continue; } else { msg->GetMatrix(transformPLUS); condition = true; break; } } } if (condition) { this->ProcessPlusCalibration(transformPLUS); } else { m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }"); m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again"); } } else { MITK_INFO << " no connection"; m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }"); m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again"); } } else { m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : red; }"); m_Controls.m_GotCalibrationLabel->setText("Something went wrong. Please try again"); } } void QmitkUltrasoundCalibration::ProcessPlusCalibration(igtl::Matrix4x4 &imageToTracker) { mitk::AffineTransform3D::Pointer imageToTrackerTransform = mitk::AffineTransform3D::New(); itk::Matrix rotationFloat = itk::Matrix(); itk::Vector translationFloat = itk::Vector(); rotationFloat[0][0] = imageToTracker[0][0]; rotationFloat[0][1] = imageToTracker[0][1]; rotationFloat[0][2] = imageToTracker[0][2]; rotationFloat[1][0] = imageToTracker[1][0]; rotationFloat[1][1] = imageToTracker[1][1]; rotationFloat[1][2] = imageToTracker[1][2]; rotationFloat[2][0] = imageToTracker[2][0]; rotationFloat[2][1] = imageToTracker[2][1]; rotationFloat[2][2] = imageToTracker[2][2]; translationFloat[0] = imageToTracker[0][3]; translationFloat[1] = imageToTracker[1][3]; translationFloat[2] = imageToTracker[2][3]; imageToTrackerTransform->SetTranslation(translationFloat); imageToTrackerTransform->SetMatrix(rotationFloat); m_CombinedModality->SetCalibration(imageToTrackerTransform); m_Controls.m_ToolBox->setItemEnabled(2, true); m_Controls.m_SavePlusCalibration->setEnabled(true); m_Controls.m_GotCalibrationLabel->setStyleSheet("QLabel { color : green; }"); m_Controls.m_GotCalibrationLabel->setText("Recieved Calibration from PLUS you can now save it"); } void QmitkUltrasoundCalibration::OnStopCalibrationProcess() { this->ClearTemporaryMembers(); m_Timer->stop(); this->GetDataStorage()->Remove(m_Node); m_Node = nullptr; this->GetDataStorage()->Remove(m_CalibNode); m_CalibNode = nullptr; this->GetDataStorage()->Remove(m_WorldNode); m_WorldNode = nullptr; m_Controls.m_ToolBox->setCurrentIndex(0); } void QmitkUltrasoundCalibration::OnDeviceServiceEvent(const ctkServiceEvent event) { if (m_CombinedModality.IsNull() || event.getType() != ctkServiceEvent::MODIFIED) { return; } ctkServiceReference service = event.getServiceReference(); QString curDepth = service.getProperty(QString::fromStdString(mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH)).toString(); if (m_CurrentDepth != curDepth) { m_CurrentDepth = curDepth; this->OnReset(); } } void QmitkUltrasoundCalibration::OnAddCalibPoint() { auto world = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); this->m_CalibPointsImage->InsertPoint(m_CalibPointsCount, world); this->m_CalibPointsTool->InsertPoint(m_CalibPointsCount, this->m_FreezePoint); QString text = text.number(m_CalibPointsCount + 1); text = "Point " + text; this->m_Controls.m_CalibPointList->addItem(text); m_CalibPointsCount++; SwitchFreeze(); } void QmitkUltrasoundCalibration::OnCalibration() { if (m_CombinedModality.IsNull()) { return; } // Compute transformation vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetSourceLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints()); transform->SetTargetLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsTool)->GetPoints()); if (!m_CombinedModality->GetIsTrackedUltrasoundActive()) { if (m_Controls.m_ScaleTransform->isChecked()) { transform->SetModeToSimilarity(); } // use affine transform else { transform->SetModeToRigidBody(); } // use similarity transform: scaling is not touched MITK_INFO << "TEST"; } else { transform->SetModeToRigidBody(); // use similarity transform: scaling is not touched } transform->Modified(); transform->Update(); // Convert from vtk to itk data types itk::Matrix rotationFloat = itk::Matrix(); itk::Vector translationFloat = itk::Vector(); vtkSmartPointer m = transform->GetMatrix(); rotationFloat[0][0] = m->GetElement(0, 0); rotationFloat[0][1] = m->GetElement(0, 1); rotationFloat[0][2] = m->GetElement(0, 2); rotationFloat[1][0] = m->GetElement(1, 0); rotationFloat[1][1] = m->GetElement(1, 1); rotationFloat[1][2] = m->GetElement(1, 2); rotationFloat[2][0] = m->GetElement(2, 0); rotationFloat[2][1] = m->GetElement(2, 1); rotationFloat[2][2] = m->GetElement(2, 2); translationFloat[0] = m->GetElement(0, 3); translationFloat[1] = m->GetElement(1, 3); translationFloat[2] = m->GetElement(2, 3); mitk::PointSet::Pointer ImagePointsTransformed = m_CalibPointsImage->Clone(); this->ApplyTransformToPointSet(ImagePointsTransformed, transform); mitk::DataNode::Pointer CalibPointsImageTransformed = this->GetDataStorage()->GetNamedNode("Calibration Points Image (Transformed)"); if (CalibPointsImageTransformed.IsNull()) { CalibPointsImageTransformed = mitk::DataNode::New(); CalibPointsImageTransformed->SetName("Calibration Points Image (Transformed)"); this->GetDataStorage()->Add(CalibPointsImageTransformed); } CalibPointsImageTransformed->SetData(ImagePointsTransformed); // Set new calibration transform m_Transformation = mitk::AffineTransform3D::New(); m_Transformation->SetTranslation(translationFloat); m_Transformation->SetMatrix(rotationFloat); MITK_INFO << "New Calibration transform: " << m_Transformation; mitk::SlicedGeometry3D::Pointer sliced3d = dynamic_cast(m_Node->GetData()->GetGeometry()); mitk::PlaneGeometry::Pointer plane = const_cast(sliced3d->GetPlaneGeometry(0)); plane->SetIndexToWorldTransform(m_Transformation); // Save to US-Device m_CombinedModality->SetCalibration(m_Transformation); m_Controls.m_ToolBox->setItemEnabled(2, true); // Save to NeedleProjectionFilter m_NeedleProjectionFilter->SetTargetPlane(m_Transformation); // Update Calibration FRE m_CalibrationStatistics = mitk::PointSetDifferenceStatisticsCalculator::New(); mitk::PointSet::Pointer p1 = this->m_CalibPointsTool->Clone(); // We use clones to calculate statistics to avoid concurrency Problems // Create point set with transformed image calibration points for // calculating the difference of image calibration and tool // calibration points in one geometry space mitk::PointSet::Pointer p2 = mitk::PointSet::New(); int n = 0; for (mitk::PointSet::PointsConstIterator it = m_CalibPointsImage->Begin(); it != m_CalibPointsImage->End(); ++it, ++n) { p2->InsertPoint(n, m_Transformation->TransformPoint(it->Value())); } m_CalibrationStatistics->SetPointSets(p1, p2); // QString text = text.number(m_CalibrationStatistics->GetRMS()); QString text = QString::number(ComputeFRE(m_CalibPointsImage, m_CalibPointsTool, transform)); MITK_INFO << "Calibration FRE: " << text.toStdString().c_str(); m_Controls.m_EvalLblCalibrationFRE->setText(text); m_Node->SetStringProperty("Calibration FRE", text.toStdString().c_str()); // Enable Button to save Calibration m_Controls.m_CalibBtnSaveCalibration->setEnabled(true); } void QmitkUltrasoundCalibration::OnLoadPhantomConfiguration() { // clear all data ClearTemporaryMembers(); // reset UI m_Controls.m_CalibBtnMatchAnnotationToPhantomConfiguration->setEnabled(false); m_Controls.m_RefinePhantomAnnotationsGroupBox->setEnabled(false); m_Controls.m_CalibBtnPerformPhantomCalibration->setEnabled(false); m_Controls.m_CalibBtnSavePhantomCalibration->setEnabled(false); // open phantom configuration QString fileName = QFileDialog::getOpenFileName(nullptr, "Load phantom configuration", "", "*.mps"); // dialog closed or selection canceled if (fileName.isNull()) { return; } m_PhantomConfigurationPointSet = dynamic_cast(mitk::IOUtil::Load(fileName.toStdString()).at(0).GetPointer()); // transform phantom fiducials to tracking space mitk::NavigationData::Pointer currentSensorData = this->m_Tracker->GetOutput(0)->Clone(); for (int i = 0; i < m_PhantomConfigurationPointSet->GetSize(); i++) { mitk::Point3D phantomPoint = m_PhantomConfigurationPointSet->GetPoint(i); mitk::Point3D transformedPoint = currentSensorData->TransformPoint(phantomPoint); this->m_CalibPointsTool->InsertPoint(i, transformedPoint); } // add point set interactor for image calibration points mitk::PointSetDataInteractor::Pointer imageCalibrationPointSetInteractor = mitk::PointSetDataInteractor::New(); imageCalibrationPointSetInteractor->LoadStateMachine("PointSet.xml"); imageCalibrationPointSetInteractor->SetEventConfig("PointSetConfig.xml"); imageCalibrationPointSetInteractor->SetDataNode(m_WorldNode); imageCalibrationPointSetInteractor->SetMaxPoints(m_PhantomConfigurationPointSet->GetSize()); // Call On AddCalibPointPhantomBased() when point was added itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkUltrasoundCalibration::OnPhantomCalibPointsChanged); m_CalibPointsImage->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); m_CalibPointsImage->AddObserver(mitk::PointSetMoveEvent(), pointAddedCommand); // Set size of image points points m_WorldNode->ReplaceProperty("point 2D size", mitk::FloatProperty::New(10.0)); } void QmitkUltrasoundCalibration::OnPhantomCalibPointsChanged() { int currentIndex = m_CalibPointsImage->SearchSelectedPoint(); mitk::Point3D currentImagePoint = m_CalibPointsImage->GetPoint(currentIndex); UpdatePhantomAnnotationPointVisualization(currentIndex); // create sphere to show radius in which next point has to be placed this->GetDataStorage()->Remove(this->GetDataStorage()->GetNamedNode("NextPointIndicator")); if (currentIndex < m_CalibPointsTool->GetSize() - 1) { float distanceToNextPoint = m_CalibPointsTool->GetPoint(currentIndex).EuclideanDistanceTo(m_CalibPointsTool->GetPoint(currentIndex + 1)); vtkSmartPointer vtkHelperSphere = vtkSmartPointer::New(); vtkHelperSphere->SetCenter(currentImagePoint[0], currentImagePoint[1], currentImagePoint[2]); vtkHelperSphere->SetRadius(distanceToNextPoint); vtkHelperSphere->SetPhiResolution(40); vtkHelperSphere->SetThetaResolution(40); vtkHelperSphere->Update(); mitk::Surface::Pointer helperSphere = mitk::Surface::New(); helperSphere->SetVtkPolyData(vtkHelperSphere->GetOutput()); mitk::DataNode::Pointer helperSphereNode = mitk::DataNode::New(); helperSphereNode->SetName("NextPointIndicator"); helperSphereNode->SetData(helperSphere); helperSphereNode->SetColor(0.0, 1.0, 0.0); this->GetDataStorage()->Add(helperSphereNode); } if (m_CalibPointsTool->GetSize() == m_CalibPointsImage->GetSize()) { m_Controls.m_CalibBtnMatchAnnotationToPhantomConfiguration->setEnabled(true); } } void QmitkUltrasoundCalibration::UpdatePhantomAnnotationPointVisualization(int index) { mitk::Point3D currentImagePoint = m_CalibPointsImage->GetPoint(index); // create sphere to show current fiducial std::stringstream pointName; pointName << "Point"; pointName << index; this->GetDataStorage()->Remove(this->GetDataStorage()->GetNamedNode(pointName.str())); vtkSmartPointer vtkPointSphere = vtkSmartPointer::New(); vtkPointSphere->SetCenter(currentImagePoint[0], currentImagePoint[1], currentImagePoint[2]); vtkPointSphere->SetRadius(5.0); vtkPointSphere->SetPhiResolution(40); vtkPointSphere->SetThetaResolution(40); vtkPointSphere->Update(); mitk::Surface::Pointer pointSphere = mitk::Surface::New(); pointSphere->SetVtkPolyData(vtkPointSphere->GetOutput()); mitk::DataNode::Pointer sphereNode = mitk::DataNode::New(); sphereNode->SetName(pointName.str()); sphereNode->SetData(pointSphere); sphereNode->SetColor(1.0, 1.0, 0.0); this->GetDataStorage()->Add(sphereNode); } void QmitkUltrasoundCalibration::OnMatchAnnotationToPhantomConfiguration() { // Transform pointset of phantom configuration to currently annotated image points vtkSmartPointer transform = vtkSmartPointer::New(); transform->SetModeToRigidBody(); vtkSmartPointer toolLandmarks = this->ConvertPointSetToVtkPolyData(m_CalibPointsTool)->GetPoints(); transform->SetSourceLandmarks(toolLandmarks); transform->SetTargetLandmarks(this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints()); transform->Update(); // update image annotation with matched phantom configuration vtkSmartPointer transformedToolLandmarks = vtkSmartPointer::New(); transform->TransformPoints(toolLandmarks, transformedToolLandmarks); for (int i = 0; i < transformedToolLandmarks->GetNumberOfPoints(); i++) { m_CalibPointsImage->InsertPoint(i, transformedToolLandmarks->GetPoint(i)); UpdatePhantomAnnotationPointVisualization(i); } m_Controls.m_RefinePhantomAnnotationsGroupBox->setEnabled(true); m_Controls.m_CalibBtnPerformPhantomCalibration->setEnabled(true); } void QmitkUltrasoundCalibration::TranslatePhantomAnnotations(double tx, double ty, double tz) { vtkSmartPointer transform = vtkSmartPointer::New(); transform->Translate(tx, ty, tz); vtkSmartPointer currentPoints = this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints(); vtkSmartPointer transformedPoints = vtkSmartPointer::New(); transform->TransformPoints(currentPoints, transformedPoints); for (int i = 0; i < transformedPoints->GetNumberOfPoints(); i++) { m_CalibPointsImage->InsertPoint(i, transformedPoints->GetPoint(i)); UpdatePhantomAnnotationPointVisualization(i); } } void QmitkUltrasoundCalibration::RotatePhantomAnnotations(double angle) { vtkSmartPointer transform = vtkSmartPointer::New(); transform->RotateZ(angle); vtkSmartPointer currentPoints = this->ConvertPointSetToVtkPolyData(m_CalibPointsImage)->GetPoints(); vtkSmartPointer transformedPoints = vtkSmartPointer::New(); transform->TransformPoints(currentPoints, transformedPoints); for (int i = 0; i < transformedPoints->GetNumberOfPoints(); i++) { m_CalibPointsImage->InsertPoint(i, transformedPoints->GetPoint(i)); UpdatePhantomAnnotationPointVisualization(i); } } void QmitkUltrasoundCalibration::OnMovePhantomAnnotationsUp() { this->TranslatePhantomAnnotations(0, -m_Image->GetGeometry()->GetSpacing()[1], 0); } void QmitkUltrasoundCalibration::OnMovePhantomAnnotationsDown() { this->TranslatePhantomAnnotations(0, m_Image->GetGeometry()->GetSpacing()[1], 0); } void QmitkUltrasoundCalibration::OnMovePhantomAnnotationsLeft() { this->TranslatePhantomAnnotations(-m_Image->GetGeometry()->GetSpacing()[0], 0, 0); } void QmitkUltrasoundCalibration::OnMovePhantomAnnotationsRight() { this->TranslatePhantomAnnotations(m_Image->GetGeometry()->GetSpacing()[0], 0, 0); } void QmitkUltrasoundCalibration::OnRotatePhantomAnnotationsRight() { mitk::BoundingBox::PointType centerOfPointSet = m_CalibPointsImage->GetGeometry()->GetBoundingBox()->GetCenter(); this->TranslatePhantomAnnotations(-centerOfPointSet[0], -centerOfPointSet[1], -centerOfPointSet[2]); this->RotatePhantomAnnotations(0.5); this->TranslatePhantomAnnotations(centerOfPointSet[0], centerOfPointSet[1], centerOfPointSet[2]); } void QmitkUltrasoundCalibration::OnRotatePhantomAnnotationsLeft() { mitk::BoundingBox::PointType centerOfPointSet = m_CalibPointsImage->GetGeometry()->GetBoundingBox()->GetCenter(); this->TranslatePhantomAnnotations(-centerOfPointSet[0], -centerOfPointSet[1], -centerOfPointSet[2]); this->RotatePhantomAnnotations(-0.5); this->TranslatePhantomAnnotations(centerOfPointSet[0], centerOfPointSet[1], centerOfPointSet[2]); } void QmitkUltrasoundCalibration::OnPhantomBasedCalibration() { // perform calibration OnCalibration(); m_Controls.m_CalibBtnSavePhantomCalibration->setEnabled(true); } void QmitkUltrasoundCalibration::OnAddEvalTargetPoint() { auto world = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); this->m_EvalPointsImage->InsertPoint(m_EvalPointsImage->GetSize(), world); this->m_EvalPointsTool->InsertPoint(m_EvalPointsTool->GetSize(), this->m_FreezePoint); QString text = text.number(this->m_EvalPointsTool->GetSize()); this->m_Controls.m_EvalLblNumTargetPoints->setText(text); // Update FREs // Update Evaluation FRE, but only if it contains more than one point (will crash otherwise) if ((m_EvalPointsProjected->GetSize() > 1) && (m_EvalPointsTool->GetSize() > 1)) { m_EvaluationStatistics = mitk::PointSetDifferenceStatisticsCalculator::New(); m_ProjectionStatistics = mitk::PointSetDifferenceStatisticsCalculator::New(); mitk::PointSet::Pointer p1 = this->m_EvalPointsTool->Clone(); // We use clones to calculate statistics to avoid concurrency Problems mitk::PointSet::Pointer p2 = this->m_EvalPointsImage->Clone(); mitk::PointSet::Pointer p3 = this->m_EvalPointsProjected->Clone(); m_EvaluationStatistics->SetPointSets(p1, p2); m_ProjectionStatistics->SetPointSets(p1, p3); QString evalText = evalText.number(m_EvaluationStatistics->GetRMS()); QString projText = projText.number(m_ProjectionStatistics->GetRMS()); m_Controls.m_EvalLblEvaluationFRE->setText(evalText); m_Controls.m_EvalLblProjectionFRE->setText(projText); } SwitchFreeze(); } void QmitkUltrasoundCalibration::OnAddEvalProjectedPoint() { MITK_WARN << "Projection Evaluation may currently be inaccurate."; // TODO: Verify correct Evaluation. Is the Point that is added really current? mitk::Point3D projection = this->m_NeedleProjectionFilter->GetProjection()->GetPoint(1); m_EvalPointsProjected->InsertPoint(m_EvalPointsProjected->GetSize(), projection); QString text = text.number(this->m_EvalPointsProjected->GetSize()); this->m_Controls.m_EvalLblNumProjectionPoints->setText(text); } void QmitkUltrasoundCalibration::OnSaveEvaluation() { // Filename without suffix QString filename = m_Controls.m_EvalFilePath->text() + "//" + m_Controls.m_EvalFilePrefix->text(); MITK_WARN << "CANNOT SAVE, ABORTING!"; /* not working any more TODO! mitk::PointSetWriter::Pointer psWriter = mitk::PointSetWriter::New(); psWriter->SetInput(0, m_CalibPointsImage); psWriter->SetInput(1, m_CalibPointsTool); psWriter->SetInput(2, m_EvalPointsImage); psWriter->SetInput(3, m_EvalPointsTool); psWriter->SetInput(4, m_EvalPointsProjected); psWriter->SetFileName(filename.toStdString() + ".xml"); psWriter->Write(); */ // TODO: New writer for transformations must be implemented. /* mitk::TransformationFileWriter::Pointer tWriter = mitk::TransformationFileWriter::New(); tWriter->SetInput(0, m_CalibPointsImage); tWriter->SetInput(1, m_CalibPointsTool); tWriter->SetInput(2, m_EvalPointsImage); tWriter->SetInput(3, m_EvalPointsTool); tWriter->SetInput(4, m_EvalPointsProjected); tWriter->SetOutputFilename(filename.toStdString() + ".txt"); tWriter->DoWrite(this->m_Transformation); */ } void QmitkUltrasoundCalibration::OnSaveCalibration() { m_Controls.m_GotCalibrationLabel->setText(""); QString filename = QFileDialog::getSaveFileName(QApplication::activeWindow(), "Save Calibration", "", "Calibration files *.cal"); QFile file(filename); if (!file.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate)) { MITK_WARN << "Cannot open file '" << filename.toStdString() << "' for writing."; return; } std::string calibrationSerialization = m_CombinedModality->SerializeCalibration(); QTextStream outStream(&file); outStream << QString::fromStdString(calibrationSerialization); // save additional information if (m_Controls.m_saveAdditionalCalibrationLog->isChecked()) { mitk::SceneIO::Pointer mySceneIO = mitk::SceneIO::New(); QString filenameScene = filename + "_mitkScene.mitk"; mitk::NodePredicateNot::Pointer isNotHelperObject = mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object", mitk::BoolProperty::New(true))); mitk::DataStorage::SetOfObjects::ConstPointer nodesToBeSaved = this->GetDataStorage()->GetSubset(isNotHelperObject); mySceneIO->SaveScene(nodesToBeSaved, this->GetDataStorage(), filenameScene.toStdString().c_str()); } } void QmitkUltrasoundCalibration::OnReset() { this->ClearTemporaryMembers(); if (m_Transformation.IsNull()) { m_Transformation = mitk::AffineTransform3D::New(); } m_Transformation->SetIdentity(); if (m_Node.IsNotNull() && (m_Node->GetData() != nullptr) && (m_Node->GetData()->GetGeometry() != nullptr)) { mitk::SlicedGeometry3D::Pointer sliced3d = dynamic_cast(m_Node->GetData()->GetGeometry()); mitk::PlaneGeometry::Pointer plane = const_cast(sliced3d->GetPlaneGeometry(0)); plane->SetIndexToWorldTransform(m_Transformation); } QString text1 = text1.number(this->m_EvalPointsTool->GetSize()); this->m_Controls.m_EvalLblNumTargetPoints->setText(text1); QString text2 = text2.number(this->m_EvalPointsProjected->GetSize()); this->m_Controls.m_EvalLblNumProjectionPoints->setText(text2); } void QmitkUltrasoundCalibration::Update() { // Update Tracking Data std::vector *datas = new std::vector(); datas->push_back(m_Tracker->GetOutput()); m_Controls.m_CalibTrackingStatus->SetNavigationDatas(datas); m_Controls.m_CalibTrackingStatus->Refresh(); m_Controls.m_EvalTrackingStatus->SetNavigationDatas(datas); m_Controls.m_EvalTrackingStatus->Refresh(); m_CombinedModality->Modified(); m_CombinedModality->Update(); // Update US Image mitk::Image::Pointer image = m_CombinedModality->GetOutput(); // make sure that always the current image is set to the data node if (image.IsNotNull() && m_Node->GetData() != image.GetPointer() && image->IsInitialized()) { m_Node->SetData(image); } // Update Needle Projection m_NeedleProjectionFilter->Update(); // only update 2d window because it is faster this->RequestRenderWindowUpdate(mitk::RenderingManager::REQUEST_UPDATE_2DWINDOWS); } void QmitkUltrasoundCalibration::SwitchFreeze() { m_Controls.m_CalibBtnAddPoint->setEnabled(false); // generally deactivate // We use the activity state of the timer to determine whether we are currently viewing images if (!m_Timer->isActive()) // Activate Imaging { // if (m_Node) m_Node->ReleaseData(); if (m_CombinedModality.IsNull()) { m_Timer->stop(); return; } m_CombinedModality->Update(); m_Image = m_CombinedModality->GetOutput(); if (m_Image.IsNotNull() && m_Image->IsInitialized()) { m_Node->SetData(m_Image); } std::vector datas; datas.push_back(m_Tracker->GetOutput()); m_Controls.m_CalibTrackingStatus->SetNavigationDatas(&datas); m_Controls.m_CalibTrackingStatus->ShowStatusLabels(); m_Controls.m_CalibTrackingStatus->Refresh(); m_Controls.m_EvalTrackingStatus->SetNavigationDatas(&datas); m_Controls.m_EvalTrackingStatus->ShowStatusLabels(); m_Controls.m_EvalTrackingStatus->Refresh(); int interval = 40; m_Timer->setInterval(interval); m_Timer->start(); m_CombinedModality->SetIsFreezed(false); } else if (this->m_Tracker->GetOutput(0)->IsDataValid()) { // deactivate Imaging m_Timer->stop(); // Remember last tracking coordinates m_FreezePoint = this->m_Tracker->GetOutput(0)->GetPosition(); m_Controls.m_CalibBtnAddPoint->setEnabled(true); // activate only, if valid point is set m_CombinedModality->SetIsFreezed(true); } } void QmitkUltrasoundCalibration::ShowNeedlePath() { // Init Filter this->m_NeedleProjectionFilter->SelectInput(0); // Create Node for Pointset mitk::DataNode::Pointer node = this->GetDataStorage()->GetNamedNode("Needle Path"); if (node.IsNull()) { node = mitk::DataNode::New(); node->SetName("Needle Path"); node->SetData(m_NeedleProjectionFilter->GetProjection()); node->SetBoolProperty("show contour", true); this->GetDataStorage()->Add(node); } } void QmitkUltrasoundCalibration::ClearTemporaryMembers() { m_CalibPointsTool->Clear(); m_CalibPointsImage->Clear(); m_CalibPointsCount = 0; m_EvalPointsImage->Clear(); m_EvalPointsTool->Clear(); m_EvalPointsProjected->Clear(); this->m_Controls.m_CalibPointList->clear(); m_SpacingPoints->Clear(); m_Controls.m_SpacingPointsList->clear(); m_SpacingPointsCount = 0; } vtkSmartPointer QmitkUltrasoundCalibration::ConvertPointSetToVtkPolyData(mitk::PointSet::Pointer PointSet) { vtkSmartPointer returnValue = vtkSmartPointer::New(); vtkSmartPointer points = vtkSmartPointer::New(); for (int i = 0; i < PointSet->GetSize(); i++) { double point[3] = {PointSet->GetPoint(i)[0], PointSet->GetPoint(i)[1], PointSet->GetPoint(i)[2]}; points->InsertNextPoint(point); } vtkSmartPointer temp = vtkSmartPointer::New(); temp->SetPoints(points); vtkSmartPointer vertexFilter = vtkSmartPointer::New(); vertexFilter->SetInputData(temp); vertexFilter->Update(); returnValue->ShallowCopy(vertexFilter->GetOutput()); return returnValue; } double QmitkUltrasoundCalibration::ComputeFRE(mitk::PointSet::Pointer imageFiducials, mitk::PointSet::Pointer realWorldFiducials, vtkSmartPointer transform) { if (imageFiducials->GetSize() != realWorldFiducials->GetSize()) return -1; double FRE = 0; for (int i = 0; i < imageFiducials->GetSize(); ++i) { itk::Point current_image_fiducial_point = imageFiducials->GetPoint(i); if (transform != nullptr) { current_image_fiducial_point = transform->TransformPoint( imageFiducials->GetPoint(i)[0], imageFiducials->GetPoint(i)[1], imageFiducials->GetPoint(i)[2]); } double cur_error_squared = current_image_fiducial_point.SquaredEuclideanDistanceTo(realWorldFiducials->GetPoint(i)); FRE += cur_error_squared; } FRE = sqrt(FRE / (double)imageFiducials->GetSize()); return FRE; } void QmitkUltrasoundCalibration::ApplyTransformToPointSet(mitk::PointSet::Pointer pointSet, vtkSmartPointer transform) { for (int i = 0; i < pointSet->GetSize(); ++i) { itk::Point current_point_transformed = itk::Point(); current_point_transformed = transform->TransformPoint(pointSet->GetPoint(i)[0], pointSet->GetPoint(i)[1], pointSet->GetPoint(i)[2]); pointSet->SetPoint(i, current_point_transformed); } } void QmitkUltrasoundCalibration::OnFreezeClicked() { if (m_CombinedModality.IsNull()) { return; } if (m_CombinedModality->GetIsFreezed()) { // device was already frozen so we need to delete all spacing points because they need to be collected all at once // no need to check if all four points are already collected, because if that's the case you can no longer click the // Freeze button m_SpacingPoints->Clear(); m_Controls.m_SpacingPointsList->clear(); m_SpacingPointsCount = 0; m_Controls.m_SpacingAddPoint->setEnabled(false); } else { m_Controls.m_SpacingAddPoint->setEnabled(true); } SwitchFreeze(); } void QmitkUltrasoundCalibration::OnAddSpacingPoint() { auto point = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN)->GetSelectedPosition(); this->m_SpacingPoints->InsertPoint(m_SpacingPointsCount, point); QString text = text.number(m_SpacingPointsCount + 1); text = "Point " + text; this->m_Controls.m_SpacingPointsList->addItem(text); m_SpacingPointsCount++; if (m_SpacingPointsCount == 4) // now we have all 4 points needed { m_Controls.m_SpacingAddPoint->setEnabled(false); m_Controls.m_CalculateSpacing->setEnabled(true); m_Controls.m_SpacingBtnFreeze->setEnabled(false); } } void QmitkUltrasoundCalibration::OnCalculateSpacing() { mitk::Point3D horizontalOne = m_SpacingPoints->GetPoint(0); mitk::Point3D horizontalTwo = m_SpacingPoints->GetPoint(1); mitk::Point3D verticalOne = m_SpacingPoints->GetPoint(2); mitk::Point3D verticalTwo = m_SpacingPoints->GetPoint(3); // Get the distances between the points in the image double xDistance = horizontalOne.EuclideanDistanceTo(horizontalTwo); double yDistance = verticalOne.EuclideanDistanceTo(verticalTwo); // Calculate the spacing of the image and fill a vector with it double xSpacing = 30 / xDistance; double ySpacing = 20 / yDistance; m_CombinedModality->GetUltrasoundDevice()->SetSpacing(xSpacing, ySpacing); // Now that the spacing is set clear all stuff and return to Calibration m_SpacingPoints->Clear(); m_Controls.m_SpacingPointsList->clear(); m_SpacingPointsCount = 0; m_CombinedModality->SetIsFreezed(false); } void QmitkUltrasoundCalibration::OnUSDepthChanged(const std::string &key, const std::string &) { // whenever depth of USImage is changed the spacing should no longer be overwritten if (key == mitk::USDevice::GetPropertyKeys().US_PROPKEY_BMODE_DEPTH) { } } 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 4fb59d9e64..aca4f168a7 100644 --- a/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp +++ b/Plugins/org.mitk.gui.qt.imagecropper/src/internal/QmitkImageCropperView.cpp @@ -1,503 +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 #include #include #include #include #include #include #include #include #include #include #include #include #include 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::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::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) { bool rotationEnabled = false; auto imageNode = m_Controls.imageSelectionWidget->GetSelectedNode(); if (imageNode.IsNull()) { SetDefaultGUI(); return; } auto image = dynamic_cast(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 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::min()) { minPixelValue = std::numeric_limits::min(); } if (maxPixelValue > std::numeric_limits::max()) { maxPixelValue = std::numeric_limits::max(); } m_Controls.spinBoxOutsidePixelValue->setEnabled(true); m_Controls.spinBoxOutsidePixelValue->setMaximum(static_cast(maxPixelValue)); m_Controls.spinBoxOutsidePixelValue->setMinimum(static_cast(minPixelValue)); m_Controls.spinBoxOutsidePixelValue->setValue(static_cast(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) { 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(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"); 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 auto timePoint = renderWindowPart->GetSelectedTimePoint(); const auto imageGeometry = imageNode->GetData()->GetTimeGeometry()->GetGeometryForTimePoint(timePoint); auto boundingBox = mitk::GeometryData::New(); boundingBox->SetGeometry(static_cast(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->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(imageNode->GetData()); auto boundingBox = dynamic_cast(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(imageNode->GetName() + "_" + boundingBoxNode->GetName() + "_masked"); + imageName = QString::fromStdString(imagePrefix + boundingBoxNode->GetName() + "_masked"); } else { - imageName = QString::fromStdString(imageNode->GetName() + "_" + boundingBoxNode->GetName() + "_cropped"); + 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(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.labelWarningRotation->setVisible(false); 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.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentation.dox b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentation.dox index c0f9d94840..bfd845dd11 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentation.dox +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentation.dox @@ -1,118 +1,99 @@ /** \page org_mitk_views_multilabelsegmentation The Multilabel Segmentation -\imageMacro{"multilabelsegmentation-dox.svg", "Icon of the MultiLabelSegmentation Plugin", 12} +\imageMacro{multilabelsegmentation_utilities.svg, "Icon of the Multilabel Segmentation Utilities Plugin", 5.00} -The difference between a binary and a multi-label segmentation is that a multi-label segmentation can not only contain more than one label but also more than one layer. This allows you to create different labels for different regions of interest encapsulated in one single image. The difference between labels and layers is that labels on one layer cannot overlap but labels on different layers can. -
-The Multilabel Segmentation plugin consists of two views which can be used for: +The Multilabel Segmentation plugin consists of one view which can be used for:
    -
  • manual and (semi-)automatic segmentation of anatomical and pathological structures in medical images via the Multilabel Segmentation View -
  • multilabel segmentation postprocessing via \subpage org_mitk_views_multilabelsegmentationutilities +
  • multilabel segmentation postprocessing
-Hereinafter, a description is given on how multi-label segmentations can be created and modified. Please refer to \ref org_mitk_views_segmentation for a description of the general segmentation tools. +Hereinafter, a description is given on how multilabel segmentations can be modified. +Please refer to \ref org_mitk_views_segmentation for a description of the general segmentation tools. \tableofcontents -\section org_mitk_views_multilabelsegmentationUserManualCreateOpenSaveImportAdd Data Selection +\section org_mitk_views_multilabelsegmentationutilitiesoverview Overview -In the 'MultiLabel Segmentation' view you can first select an image. Thereafter, you can either create a new segmentation session by pressing the symbol right to the selection widget or select an existing one. - -\imageMacro{"org_mitk_views_multilabelsegmentationIMGtoolbar.png", "Data selection",12} - - -\section org_mitk_views_multilabelsegmentationUserManualLayerTable Segmentation Layers -Once you started a segmentation session you can add or delete different layers. You can use the layers independently and switch layers by the left and right arrows. - - -\imageMacro{"org_mitk_views_multilabelsegmentationIMGlayerManager.png", "Layer selection",12} - -\subsection org_mitk_views_multilabelsegmentationUserManualLayerCreation Creating a New Layer - -A layer is a set of labels that occupy a non-overlapping anatomical space. The best way to describe them is by a real use case. Imagine you are working on a radiotherapy planning application. In the first layer of your segmentation -session, you would like to trace the contours of the liver and neighboring organs. You can accommodate all these segmentations in separate labels because they all occupy different anatomical regions and do not overlap. Now say you would like to segment the arteries and veins inside the liver. If you don't trace them in a different layer, you will overwrite the previous ones. You may also need a third layer for segmenting the different irrigation territories in the liver and a fourth layer to contain the lesion you would like to treat. - -\section org_mitk_views_multilabelsegmentationUserManualLabelTable Segmentation Labels - -For each layer, you can add one or more labels. Pressing the double arrow on the right, all created labels are shown in the 'Lable Table'. The following label properties are available: +The Multilabel Segmentation Utilities Plugin allows to postprocess existing segmentations. Currently five different operations exist:
    -
  • Name:
  • the name of the label. Can be a predefined one or any other. -
  • Locked:
  • whether the label is locked or editable. A locked label cannot be overwritten by another. -
  • Color:
  • the color of the label. -
  • Visible:
  • whether the label is currently visible or hidden. +
  • \ref org_mitk_views_multilabelsegmentationutilitiesbooleanoperations +
  • \ref org_mitk_views_multilabelsegmentationutilitiescontourtoimage +
  • \ref org_mitk_views_multilabelsegmentationutilitiesimagemasking +
  • \ref org_mitk_views_multilabelsegmentationutilitiesmorphologicaloperations +
  • \ref org_mitk_views_multilabelsegmentationutilitiessurfacetoimage +
  • \ref org_mitk_views_multilabelsegmentationutilitiesconverttomultilabel
+\imageMacro{QmitkMultiLabelSegmentationUtilities_Overview.png, "The Multilabel Segmentation Utilities View",16.00} -\imageMacro{"org_mitk_views_multilabelsegmentationIMGlabeltable.png", "The 'Label Table' shows all labels in the current segmentation session", 12} - -\subsection org_mitk_views_multilabelsegmentationUserManualLabelCreation Creating a New Label +\section org_mitk_views_multilabelsegmentationutilitiesdataselection Data Selection -Click the 'New Label' button to add a new label. A dialog will show up to enter the name and color. Preset organ names and -corresponding colors are offered while you type in, but you can set any name. The new name - if not known - will be automatically remembered and made available the next time you create a new label. In the current implementation of the plugin, the maximum number of labels is restricted to 255. If you need more, you will have to create a new segmentation session. +All postprocessing operations provide one or more selection widgets, which allow to select the data for the operation. +\section org_mitk_views_multilabelsegmentationutilitiesbooleanoperations Boolean operations -\subsection org_mitk_views_multilabelsegmentationUserManualLabelPresets Saving and Loading Label Set Presets +Boolean operations allow to perform the following fundamental operations on two segmentations: -Label set presets are useful to share a certain style or scheme between different segmentation sessions or to provide templates for new segmentation sessions. - -The properties of all labels in all layers like their names, colors, and visibilities are saved as a label set preset by clicking on the 'Save label set preset' button. -Label set presets are applied to any segmentation session by clicking on the 'Load label set preset' button. -If a label for a certain value already exists, its properties are overridden by the preset. -If a label for a certain value does not yet exist, an empty label with the label properties of the preset is created. -The actual segmentations of labels are unaffected as label set presets only store label properties. - - -\subsection org_mitk_views_multilabelsegmentationUserManualLabelSearch Searching a Label - -It may happen that many labels (e.g. > 200) are present in a segmentation session and therefore manual searching can be time-consuming. -The 'Label Search' edit box allows for quickly finding the label you want. Just start writing its name and you will get assistance for completing its name. If the label you were searching is found, press 'enter' and it will become the active one. +
    +
  • Difference: Subtracts the second segmentation from the first segmentation. +
  • Intersection: Extracts the overlapping areas of the two selected segmentations. +
  • Union: Combines the two existing segmentations. +
+The selected segmentations must have the same geometry (size, spacing, ...) in order for the operations to work correctly. +The result will be stored in a new data node as a child node of the first selected segmentation. -\subsection org_mitk_views_multilabelsegmentationUserManualLabelEditing Label Editing +\imageMacro{QmitkMultiLabelSegmentationUtilities_BooleanOperations.png,"Boolean operations",6.00} -Here the actual segmentation takes place. First of all, you have to select the active label by clicking on the corresponding row in the 'Label Table'. Note that only one label can be active at the time. Then you can select segmentation tool in the toolbox. +\section org_mitk_views_multilabelsegmentationutilitiescontourtoimage Contour to image -\imageMacro{"org_mitk_views_multilabelsegmentationIMGSegmentationToolbox.png", "Segmentation toolbox", 12} +Contour to image allows to create a segmentation out of a given contour-model. +The operation requires a contour model set and a reference image. +The created segmentation image will have the same geometrical properties like the reference image (dimension, size and Geometry3D). +\imageMacro{QmitkMultiLabelSegmentationUtilities_ContourToImage.png,"Contour to image",6.00} -\note Not all segmentation tools can be found here. This is because some of the semi-automatic tools can not be applied in case of multiple labels. +\section org_mitk_views_multilabelsegmentationutilitiesimagemasking Image masking -\subsection org_mitk_views_multilabelsegmentationUserManualOperationsOnLabels Operations on Labels +Image masking allows to mask an image with either an existing segmentation or a surface. +The operation requires an image and a binary image mask or a surface. +The result will be an image containing only the pixels that are covered by the respective mask. +Depending on the selected mode ("make output binary" or "overwrite foreground" / "overwrite background") +the result will be a simple image or a binary image. -Depending on your selection in the 'Label Table', several actions are offered: +\imageMacro{QmitkMultiLabelSegmentationUtilities_ImageMasking.png,"Image masking",6.00} -\subsubsection org_mitk_views_multilabelsegmentationUserManualOperationsOnSingleSelection Single Label Selection +\section org_mitk_views_multilabelsegmentationutilitiesmorphologicaloperations Morphological operations -If you right click on any label in the table, a pop-up menu offers the following actions to be performed on the selected label: +Morphological operations are applied to a single segmentation image. Based on a given structuring element the underlying segmentation will be modfied. +The plugin provides a ball and a cross as structuring elements. The follow operations are available:
    -
  • Rename...
  • : change the name and/or color of the selected label. -
  • Remove...
  • : delete the selected label. -
  • Erase...
  • : only clear the contents of the selected label. -
  • Merge...
  • : merge two labels by selecting a second label. -
  • Random color
  • : assign a random color to the label. -
  • View only
  • : make all labels except the current selected label invisible. -
  • View/Hide all
  • : make all labels visible / invisible -
  • Lock/Unlock all
  • : lock or unlock all labels. -
  • Create surface
  • : generate a surface out of the selected label. -
  • Create mask
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside. -
  • Create cropped mask
  • : generate a binary mask out of the selected label. Crop changes the extent of the resulting image to the extent of the label. +
  • Dilation: Each labeled pixel within the segmentation will be dilated based on the selected structuring element. +
  • Erosion: Each labeled pixel within the segmentation will be eroded based on the selected structuring element. +
  • Opening: An erosion followed by a dilation, used for filling small holes. +
  • Closing: A dilation followed by an erosion, used for smoothing edges or eliminating small objects. +
  • Fill Holes: Fills bigger holes within a segmentation.
-\imageMacro{"org_mitk_views_multilabelsegmentationIMGLabelTableSingleSelectionContextMenu.png", "Context menu for single label selection", 12} +\imageMacro{QmitkMultiLabelSegmentationUtilities_MorphologicalOperations.png,"Morphological operations",6.00} -\subsubsection org_mitk_views_multilabelsegmentationUserManualOperationsOnMultipleSelection Multiple Label Selection +\section org_mitk_views_multilabelsegmentationutilitiessurfacetoimage Surface to image -You can select more than one label by shift-click on other labels. If more than one label is selected, different options will appear in the menu: +Surface to image allows to create a segmentation out of a given surface. +The operation requires a surface and a reference image. +The created segmentation image will have the same geometrical properties like the reference image (dimension, size and Geometry3D). +Depending on the selected mode ("make output binary" or "overwrite background") +the result will be a simple image or a binary image. -\imageMacro{"org_mitk_views_multilabelsegmentationIMGLabelTableMultipleSelectionContextMenu.png", "Context menu for multiple label selection", 12} +\imageMacro{QmitkMultiLabelSegmentationUtilities_SurfaceToImage.png,"Surface to image",6.00} -
    -
  • Merge selection on current label
  • : transfer the contents of the selected labels in the 'Label Table' into the current one. -
  • Remove selected labels
  • : delete the selected labels. -
  • Erase selected labels
  • : only clear the contents of the selected labels. -
+\section org_mitk_views_multilabelsegmentationutilitiesconverttomultilabel Convert to Multilabel + +Convert to multilabel allows to interpret the pixel values of a given image as label IDs and convert the image content into the respective Multilabel image. +The new segmentation will contain one layer with as many labels as the original image contains different pixel values. +\imageMacro{QmitkMultiLabelSegmentationUtilities_ConvertToMultilabel.png,"Convert to Multilabel",6.00} */ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities.dox b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities.dox deleted file mode 100644 index 7a44044261..0000000000 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/QmitkMultiLabelSegmentationUtilities.dox +++ /dev/null @@ -1,91 +0,0 @@ -/** -\page org_mitk_views_multilabelsegmentationutilities The Multilabel Segmentation Utilities - -\imageMacro{multilabelsegmentation_utilities.svg,"Icon of the Multilabel Segmentation Utilities Plugin",5.00} - -\imageMacro{QmitkMultiLabelSegmentationUtilities_Overview.png, "The Multilabel Segmentation Utilities View",16.00} - -\tableofcontents - -\section org_mitk_views_multilabelSegmentationUtilitiesOverview Overview - -The Multilabel Segmentation Utilities Plugin allows to postprocess existing segmentations. Currently five different operations exist: - -
    -
  • \ref org_mitk_views_multilabelSegmentationUtilitiesBooleanOperations -
  • \ref org_mitk_views_multilabelSegmentationUtilitiesContourToImage -
  • \ref org_mitk_views_multilabelSegmentationUtilitiesImageMasking -
  • \ref org_mitk_views_multilabelSegmentationUtilitiesMorphologicalOperations -
  • \ref org_mitk_views_multilabelSegmentationUtilitiesSurfaceToImage -
  • \ref org_mitk_views_multilabelSegmentationUtilitiesConvertToMultiLabel -
- -\section org_mitk_views_multilabelSegmentationUtilitiesDataSelection Data Selection - -All postprocessing operations provide one or more selection widgets, which allow to select the data for the operation. - -\section org_mitk_views_multilabelSegmentationUtilitiesBooleanOperations Boolean operations - -Boolean operations allows to perform the following fundamental operations on two segmentations: - -
    -
  • Difference: Subtracts the second segmentation from the first segmentation. -
  • Intersection: Extracts the overlapping areas of the two selected segmentations. -
  • Union: Combines the two existing segmentations. -
- -The selected segmentations must have the same geometry (size, spacing, ...) in order for the operations to work correctly. -The result will be stored in a new data node as a child node of the first selected segmentation. - -\imageMacro{QmitkMultiLabelSegmentationUtilities_BooleanOperations.png,"Boolean operations",6.00} - -\section org_mitk_views_multilabelSegmentationUtilitiesContourToImage Contour to image - -Contour to image allows to create a segmentation out of a given contour-model. -The operation requires a contour model set and a reference image. -The created segmentation image will have the same geometrical properties like the reference image (dimension, size and Geometry3D). - -\imageMacro{QmitkMultiLabelSegmentationUtilities_ContourToImage.png,"Contour to image",6.00} - -\section org_mitk_views_multilabelSegmentationUtilitiesImageMasking Image masking - -Image masking allows to mask an image with either an existing segmentation or a surface. -The operation requires an image and a binary image mask or a surface. -The result will be an image containing only the pixels that are covered by the respective mask. -Depending on the selected mode ("make output binary" or "overwrite foreground" / "overwrite background") -the result will be a simple image or a binary image. - -\imageMacro{QmitkMultiLabelSegmentationUtilities_ImageMasking.png,"Image masking",6.00} - -\section org_mitk_views_multilabelSegmentationUtilitiesMorphologicalOperations Morphological operations - -Morphological operations are applied to a single segmentation image. Based on a given structuring element the underlying segmentation will be modfied. -The plugin provides a ball and a cross as structuring elements. The follow operations are available: - -
    -
  • Dilation: Each labeled pixel within the segmentation will be dilated based on the selected structuring element. -
  • Erosion: Each labeled pixel within the segmentation will be eroded based on the selected structuring element. -
  • Closing: An erosion followed by a dilation, used for filling small holes. -
  • Opening: A dilation followed by an erosion, used for smoothing edges or eliminating small objects. -
  • Fill Holes: Fills bigger holes within a segmentation. -
- -\imageMacro{QmitkMultiLabelSegmentationUtilities_MorphologicalOperations.png,"Morphological operations",6.00} - -\section org_mitk_views_multilabelSegmentationUtilitiesSurfaceToImage Surface to image - -Surface to image allows to create a segmentation out of a given surface. -The operation requires a surface and a reference image. -The created segmentation image will have the same geometrical properties like the reference image (dimension, size and Geometry3D). -Depending on the selected mode ("make output binary" or "overwrite background") -the result will be a simple image or a binary image. - -\imageMacro{QmitkMultiLabelSegmentationUtilities_SurfaceToImage.png,"Surface to image",6.00} - -\section org_mitk_views_multilabelSegmentationUtilitiesConvertToMultiLabel Convert to Multilabel - -Convert to multilabel allows to interpret the pixel values of a given image as label IDs and convert the image content into the respective Multilabel image. -The new segmentation will contain one layer with as many labels as the original image contains different pixel values. - -\imageMacro{QmitkMultiLabelSegmentationUtilities_ConvertToMultilabel.png,"Convert to Multilabel",6.00} -**/ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGSegmentationToolbox.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGSegmentationToolbox.png deleted file mode 100644 index eeb06e60d2..0000000000 Binary files a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGSegmentationToolbox.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGapplication.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGapplication.png deleted file mode 100644 index 3763903073..0000000000 Binary files a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGapplication.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGsearchlabel.png b/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGsearchlabel.png deleted file mode 100644 index 5c755101e0..0000000000 Binary files a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGsearchlabel.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake b/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake index a2f14a89c5..b7307305ef 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/files.cmake @@ -1,95 +1,85 @@ set(SRC_CPP_FILES ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkThresholdAction.cpp QmitkConvertSurfaceToLabelAction.cpp QmitkConvertMaskToLabelAction.cpp QmitkConvertToMultiLabelSegmentationAction.cpp QmitkCreateMultiLabelSegmentationAction.cpp Common/QmitkDataSelectionWidget.cpp SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.cpp SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.cpp SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.cpp ) set(UI_FILES src/internal/Common/QmitkDataSelectionWidgetControls.ui src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesViewControls.ui src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidgetControls.ui src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidgetControls.ui ) set(MOC_H_FILES src/internal/mitkPluginActivator.h src/internal/QmitkThresholdAction.h src/internal/QmitkConvertSurfaceToLabelAction.h src/internal/QmitkConvertMaskToLabelAction.h src/internal/QmitkConvertToMultiLabelSegmentationAction.h src/internal/QmitkCreateMultiLabelSegmentationAction.h src/internal/Common/QmitkDataSelectionWidget.h src/internal/SegmentationUtilities/QmitkMultiLabelSegmentationUtilitiesView.h src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.h src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h src/internal/SegmentationUtilities/ConvertToMl/QmitkConvertToMlWidget.h ) set(CACHED_RESOURCE_FILES resources/BooleanDifference_48x48.png resources/BooleanIntersection_48x48.png resources/BooleanOperations_48x48.png resources/BooleanUnion_48x48.png resources/Closing_48x48.png resources/CTKWidgets_48x48.png resources/deformablePlane.png resources/Dilate_48x48.png resources/Erode_48x48.png resources/FillHoles_48x48.png resources/Icons.svg resources/ImageMasking_48x48.png resources/MorphologicalOperations_48x48.png - resources/multilabelsegmentation.svg resources/multilabelsegmentation_utilities.svg resources/Opening_48x48.png resources/SurfaceToImage_48x48.png plugin.xml ) set(QRC_FILES - resources/multilabelsegmentation.qrc resources/MultiLabelSegmentationUtilities.qrc resources/MorphologicalOperationsWidget.qrc resources/BooleanOperationsWidget.qrc ) set(CPP_FILES) foreach(file ${SRC_CPP_FILES}) set(CPP_FILES ${CPP_FILES} src/${file}) endforeach(file ${SRC_CPP_FILES}) -#usFunctionEmbedResources( -#CPP_FILES -# LIBRARY_NAME "liborg_mitk_gui_qt_multilabelsegmentation" -#ROOT_DIR resources -#FILES Interactions/SegmentationInteraction.xml -# Interactions/ConfigSegmentation.xml -#) - 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.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc index f1f88d9114..035549a939 100644 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc +++ b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/MultiLabelSegmentationUtilities.qrc @@ -1,12 +1,11 @@ BooleanOperations_48x48.png ContourModelSetToImage_48x48.png ImageMasking_48x48.png MorphologicalOperations_48x48.png SurfaceToImage_48x48.png multilabelsegmentation_utilities.svg CTKWidgets_48x48.png - multilabelsegmentation.svg diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc deleted file mode 100644 index 2e294d12aa..0000000000 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.qrc +++ /dev/null @@ -1,5 +0,0 @@ - - - multilabelsegmentation.svg - - diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.svg b/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.svg deleted file mode 100644 index 9f74c3ac99..0000000000 --- a/Plugins/org.mitk.gui.qt.multilabelsegmentation/resources/multilabelsegmentation.svg +++ /dev/null @@ -1,10607 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Plugins/org.mitk.gui.qt.radiomics/documentation/UserManual/QmitkPhenotypingPortalPage.dox b/Plugins/org.mitk.gui.qt.radiomics/documentation/UserManual/QmitkPhenotypingPortalPage.dox index 550c6d9daf..c9afa60b63 100644 --- a/Plugins/org.mitk.gui.qt.radiomics/documentation/UserManual/QmitkPhenotypingPortalPage.dox +++ b/Plugins/org.mitk.gui.qt.radiomics/documentation/UserManual/QmitkPhenotypingPortalPage.dox @@ -1,40 +1,40 @@ /** \page org_mitk_gui_qt_mitkphenotyping The Phenotyping \tableofcontents MITK Phenotyping is a selection of algorithms that can be used to extract image-based phenotypes, for example using a radiomics approach. The software is part of the research of the Division of Medical Image Computing of the German Cancer Research Center (DKFZ). MITK Phenotyping is not intended to be a single application, it is rather a collection of the necessary plugins within the offical MITK releases. The functionality of MITK Phenotyping can be accessed in different ways: Using the graphical interface using the Plugins listed below, using command line applications, or using one of the programming interfaces. \section org_mitk_gui_qt_mitkphenotyping_Tutorials Tutorials \li \subpage org_mitk_views_radiomicstutorial_gui_portal A tutorial on how to use the grapical interface of MITK Phenotying \section org_mitk_gui_qt_mitkphenotyping_Views Views \subsection sub2 Specific Views: Views that were developed with the main focus on Radiomics. They still might be used in other use-cases as well: \li \subpage org_mitk_views_radiomicstransformationview : Image transformations like Resampling, Laplacian of Gaussian, and Wavelet Transformations \li \subpage org_mitk_views_radiomicsmaskprocessingview : Processing and Cleaning of Masks \li \subpage org_mitk_views_radiomicsarithmetricview : Processing images using mathematical operations \li \subpage org_mitk_views_radiomicsstatisticview : Calculate Radiomics Features \subsection sub1 Non-Specific Views: This section contains views that are included within MITK Phenotyping, but were developed with a broader application in mind. \li \ref org_mitk_views_basicimageprocessing : Deprecated plugin for performing different image-related tasks like subtraction, mutliplaction, filtering etc. \li \ref org_mitk_views_matchpoint_algorithm_browser : Selection of MatchPoint (Registration) Algorithm \li \ref org_mitk_views_matchpoint_algorithm_control : Configuring and Controlling MatchPoint (Registration) Algorithm \li \ref org_mitk_views_matchpoint_evaluator : Evaluate the Registration performance using MatchPoint \li \ref org_mitk_views_matchpoint_manipulator : Adapt a registration calculated using MatchPoint \li \ref org_mitk_views_matchpoint_mapper : Apply a MatchPoint Registration to a specific image \li \ref org_mitk_views_matchpoint_visualizer : Visualize a Registration obtained with MatchPoint -\li \ref org_mitk_views_multilabelsegmentation : Create and editing of Multilabel-Segmentations. -\li \ref org_mitk_views_segmentation : Create simple segmentations -\li \ref org_mitk_views_segmentationutilities : Utilities for the processing of simple segmentations. +\li \ref org_mitk_views_segmentation : Creating Multilabel Segmentations +\li \ref org_mitk_views_segmentationutilities : Utilities for the processing of simple segmentations +\li \ref org_mitk_views_multilabelsegmentation : Utilities for the processing of Multilabel Segmentations \section radiomics_miniapps MiniApps (Command line Tools) \li \subpage MiniAppExplainPage Explanation of the Command Line App concept in MITK \li \subpage mitkBasicImageProcessingMiniAppsPortalPage : List of common preprocessing MiniApps \li \subpage mitkClassificationMiniAppsPortalPage : (Incomplete) list of MITK Classification MiniApps */ diff --git a/Plugins/org.mitk.gui.qt.segmentation/CMakeLists.txt b/Plugins/org.mitk.gui.qt.segmentation/CMakeLists.txt index 6180e68ea4..dd2ab4fc65 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/CMakeLists.txt +++ b/Plugins/org.mitk.gui.qt.segmentation/CMakeLists.txt @@ -1,10 +1,10 @@ project(org_mitk_gui_qt_segmentation) include_directories(${CTK_INCLUDE_DIRS}) mitk_create_plugin( EXPORT_DIRECTIVE MITK_QT_SEGMENTATION EXPORTED_INCLUDE_SUFFIXES src - MODULE_DEPENDS MitkQtWidgetsExt MitkSegmentationUI + MODULE_DEPENDS MitkAppUtil MitkQtWidgetsExt MitkSegmentationUI PACKAGE_DEPENDS Qt5|OpenGL ) diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox index d47d701247..17cd1deb32 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox +++ b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation.dox @@ -1,306 +1,392 @@ /** \page org_mitk_views_segmentation The Segmentation View \imageMacro{segmentation-dox.svg,"Icon of the segmentation view",2.00} Some of the features described below are closed source additions to the open source toolkit MITK and are not available in every application. \tableofcontents -\section org_mitk_views_segmentationUserManualOverview Overview +\section org_mitk_views_segmentationoverview Overview Segmentation is the act of partitioning an image into subsets by either manual or automated delineation to create i.e. a distinction between foreground and background. -The MITK segmentation plugin allows you to create segmentations of anatomical and pathological structures in medical images. -The plugin consists of a number of views: +A multilabel segmentation can contain more than one label and more than one layer. +This allows you to create different labels for different regions of interest encapsulated in one single image. +The difference between labels and layers is that labels on one layer cannot overlap but labels on different layers can. + +The MITK segmentation plugin allows you to create multilabel segmentations of anatomical and pathological structures in medical images. +The plugin consists of two views:
  • Segmentation View: Manual and (semi-)automatic segmentation
  • \subpage org_mitk_views_segmentationutilities : Segmentation post-processing
In this documentation, the features and usage of the segmentation view will be described. -For an introduction to the segmentation utilities and clipping plane views, please be referred to the respective documentation pages. +For an introduction to the segmentation utilities please be referred to the respective documentation pages. \imageMacro{QmitkSegmentationPlugin_Overview.png,"Segmentation plugin overview", 16.00} -\section org_mitk_views_segmentationPreferences Preferences +\section org_mitk_views_segmentationpreferences Preferences The segmentation plugin offers a number of preferences which can be set via the MITK Workbench application preferences: \imageMacro{QmitkSegmentationPreferences.png,"Segmentation preferences", 10.00}
    -
  • Slim view: Allows you to show or hide the tool button description of the segmentation view +
  • Slim view: Allows to show or hide the tool button description of the segmentation view
  • 2D display: Specify whether the segmentation is drawn as outline or as a transparent overlay -
  • 3D display: Activate 3D volume rendering for your segmentation -
  • Data node selection mode: If activated the segmentation image is automatically chosen from the data manager selection. +
  • Show only selected nodes: Enable if only the selected segmentation and the reference image should be visible
  • Smoothed surface creation: Set certain smoothing parameters for surface creation +
  • Default label set preset: Start a new segmentation with this preset instead of a default label
- -\section org_mitk_views_segmentationUserManualTechnical Technical Issues +\section org_mitk_views_segmentationtechnicalissues Technical issues The segmentation plugin makes a number of assumptions:
  • Images must be 2D, 3D, or 3D+t.
  • Images must be single-values, i.e. CT, MRI or "normal" ultrasound. Images from color doppler or photographic (RGB) images are only partially supported (please be aware that some tools might not be compatible with this image type). -
  • Segmentations are handled as binary images of the same extent as the original image. +
  • Segmentations are handled as multilabel images of the same extent as the original image.
-\section org_mitk_views_segmentationUserManualImageSelection Data Selection & Creating New Segmentations +\section org_mitk_views_segmentationdataselection Data selection & creating new segmentations -To select a reference image for the segmentation, click on the Patient Image selection widget and choose a suitable image from the selection available in the data manager. -By default the auto selection mode is enabled (see \ref org_mitk_views_segmentationPreferences).\n -Once a patient image is selected, a new segmentation can be created on this reference image by clicking the New... button to the right of the Segmentation selection widget. -An input field will appear which allows you to set the name and display color of the segmentation. Notice that the input field suggests names once you start typing and that it also suggests colors for known organ names. -Once generated the segmentation will be added with "binary mask" icon to the data manager as sub-node of the reference image. This item is automatically selected for you, allowing you to start editing the new segmentation right away. +To select a reference image for the segmentation, click on the Selected image selection widget and choose a suitable image from the selection available in the data manager. +Once an image is selected, a new segmentation can be created on this reference image by clicking the button to the right of the Selected segmentation selection widget. +A new multilabel segmentation with an initial label is automatically generated. +The new segmentation will be added to the data manager as sub-node of the reference image. +This item is then automatically selected in the data selection, which allows to start editing the new segmentation right away. +\imageMacro{"QmitkSegmentation_DataSelection.png","Data selection",12} -\subsection org_mitk_views_segmentationUserManualManualKringeling2 Selecting Segmentations for Editing Alternatively to creating a new segmentation, an existing one can be edited as well. -As you might have segmented multiple structures within a single image, the application needs to know which of them to use for editing. -For that you click the segmentation selection widget and a selection field will open, containing all suitable segmentations for the parent dataset available in the data manager. +If a reference image is selected for which a segmentation already exists in the data manager, the auto selection mode will automatically +select a fitting segmentation. +Clicking on the segmentation selection widget a drop down list will open, containing all suitable segmentations for the selected reference dataset available in the data manager. + +\section org_mitk_views_segmentationlayers Segmentation layers + +For each multilabel segmentation different layers can be added or deleted. The layers can be used independently and layers can be switched using the left and right arrows. +A layer is a set of labels that occupy a non-overlapping anatomical space. The best way to describe them is by a real use case: +Imagine you are working on a radiotherapy planning application. In the first layer of your segmentation session, you would like to trace the contours of the liver and neighboring organs. +You can accommodate all these segmentations in separate labels because they all occupy different anatomical regions and do not overlap. +Now say you would like to segment the arteries and veins inside the liver. If you don't trace them in a different layer, you will overwrite the previous ones. +You may also need a third layer for segmenting the different irrigation territories in the liver and a fourth layer to contain the lesion you would like to treat. + +\imageMacro{"QmitkSegmentation_LayerSelection.png","Layer selection",12} + +\section org_mitk_views_segmentationlabels Segmentation labels + +For each layer, one or more labels can be added. Pressing the double arrow on the right, all created labels are shown in the 'Label Table'. +The following label properties are available: + +
    +
  • Name:
  • the name of the label. Can be a predefined one or any other. +
  • Locked:
  • whether the label is locked or editable. A locked label cannot be overwritten by another. +
  • Color:
  • the color of the label. +
  • Visible:
  • whether the label is currently visible or hidden. +
+ +\imageMacro{"QmitkSegmentation_LabelTable.png","The 'Label Table' shows all labels in the current segmentation session",12} + +The 'New Label' button can be used to add a new label. This will automatically add a new label with a distinct name and color to the list of available labels.\n +In the current implementation of the plugin, the maximum number of labels is restricted to 255. If you need more, you will have to create a new segmentation session. + +\subsection org_mitk_views_segmentationlabelpresets Saving and loading label set presets +Label set presets are useful to share a certain style or scheme between different segmentation sessions or to provide templates for new segmentation sessions. + +The properties of all labels in all layers like their names, colors, and visibilities are saved as a label set preset by clicking on the 'Save label set preset' button. +Label set presets are applied to any segmentation session by clicking on the 'Load label set preset' button. +If a label for a certain value already exists, its properties are overridden by the preset. +If a label for a certain value does not yet exist, an empty label with the label properties of the preset is created. +The actual segmentations of labels are unaffected as label set presets only store label properties. + +\subsubsection org_mitk_views_segmentationdefaultlabelpresets Applying label set presets by default + +If you work on a repetetive segmentation task, manually loading the same label set preset for each and every new segmentation can be tedious. +To streamline your workflow, you can set a default label set preset in the Segmentation preferences (Ctrl+P). When set, this label set preset will be applied to all new segmentations instead of creating the default red "New label 1" label. + +\subsection org_mitk_views_segmentationlabelsearch Searching for a label + +It may happen that many labels (e.g. > 200) are present in a segmentation session and therefore manual searching can be time-consuming. +The 'Label Search' edit box allows for quickly finding a label by providing assistance for label name completion. +If the label is found, it will become the active one after pressing 'enter'. + +To start editing a label needs to be activated by clicking on the corresponding row in the 'Label Table'. +Only one label can be active at the time. Then the segmentation tools in the toolbox can be used for mask generation. + +\subsection org_mitk_views_multilabelsegmentationoperationsonlabels Operations on labels +Depending on the selection in the 'Label Table', several actions are offered: + +\subsubsection org_mitk_views_segmentationoperationssingleselection Operations with single label selection + +Right-clicking on any label opens a pop-up menu that offers the following actions to be performed on the selected label: + +
    +
  • Rename...
  • : change the name and / or color of the selected label. +
  • Remove...
  • : delete the selected label. +
  • Erase...
  • : only clear the contents of the selected label. +
  • Merge...
  • : merge two labels by selecting a second label. +
  • Random color
  • : assign a random color to the label. +
  • View only
  • : make all labels except the current selected label invisible. +
  • View/Hide all
  • : make all labels visible / invisible +
  • Lock/Unlock all
  • : lock or unlock all labels. +
  • Create surface
  • : generate a surface out of the selected label. +
  • Create mask
  • : generate a mask out of the selected label. A mask is a binary image with "1" inside and "0" outside. +
  • Create cropped mask
  • : generate a binary mask out of the selected label. Crop changes the extent of the resulting image to the extent of the label. +
-\section org_mitk_views_segmentationUserManualToolOverview Segmentation Tool Overview +\imageMacro{"QmitkSegmentation_OperationsSingleSelection.png","Context menu for single label selection",12} + +\subsubsection org_mitk_views_segmentationoperationsmultiselection Operations with multiple label selection + +Shift-clickink on multiple labels allows to select more than one label. If more than one label is selected, different options will appear in the menu: + +
    +
  • Merge selection on current label
  • : transfer the contents of the selected labels in the 'Label Table' into the current one. +
  • Remove selected labels
  • : delete the selected labels. +
  • Erase selected labels
  • : only clear the contents of the selected labels. +
+ +\imageMacro{"QmitkSegmentation_OperationsMultiSelection.png","Context menu for multiple label selection",12} + +\section org_mitk_views_segmentationtooloverview Segmentation tool overview MITK offers a comprehensive set of slice-based 2D and (semi-)automated 3D segmentation tools. -The manual 2D tools require some user interaction and can only be applied to a single image slice whereas the 3D tools operate on the whole image. The 3D tools usually only -require a small amount of user interaction, i.e. placing seed points or setting/adjusting parameters. +The manual 2D tools require some user interaction and can only be applied to a single image slice whereas the 3D tools operate on the whole image. +The 3D tools usually only require a small amount of user interaction, i.e. placing seed points or setting / adjusting parameters. You can switch between the different toolsets by selecting the 2D or 3D tab in the segmentation view. \imageMacro{QmitkSegmentation_ToolOverview.png,"An overview of the existing 2D and 3D tools in MITK.",5.50} -\section org_mitk_views_segmentationUserManualManualKringeling 2D Segmentation Tools +\section org_mitk_views_segmentation2dsegmentation 2D segmentation tools -With 2D manual contouring you define which voxels are part of the segmentation and which ones are not. This allows you to create segmentations of any structures of interest in an image. +With 2D manual contouring you define which voxels are part of the segmentation and which are not. This allows you to create segmentations of any structures of interest in an image. You can also use manual contouring to correct segmentations that result from sub-optimal automatic methods. The drawback of manual contouring is that you might need to define contours on many 2D slices. However, this is mitigated by the interpolation feature, which will make suggestions for a segmentation. -\subsection org_mitk_views_segmentationUserManualManualKringeling3 Selecting Editing Tools - To start using one of the editing tools, click its button from the displayed toolbox. The selected editing tool will be active and its corresponding button will stay pressed until you click the button again. Selecting a different tool also deactivates the previous one.\n -If you have to delineate a lot of images, shortcuts to switch between tools becomes convenient. For that, just hit the first letter of each tool to activate it (A for Add, S for Subtract, etc.). - -\subsection org_mitk_views_segmentationUserManualManualKringeling4 Using Editing Tools +If you have to delineate a lot of images, shortcuts to switch between tools becomes convenient. +For that, just hit the first letter of each tool to activate it (A for Add, S for Subtract, etc.). -All of the editing tools work by the same principle: you use the mouse (left button) to click anywhere in a 2D window (any of the orientations axial, sagittal, or coronal), -move the mouse while holding the mouse button and release to finish the editing action. -Multi-step undo and redo is fully supported by all editing tools. Use the application-wide undo button in the toolbar to revert erroneous %actions. +All of the editing tools work by the same principle: using the mouse (left button) to click anywhere in a 2D window (any of the orientations axial, sagittal, or coronal), +moving the mouse while holding the mouse button and releasing the button to finish the editing action. +Multi-step undo and redo is fully supported by all editing tools by using the application-wide undo / redo buttons in the toolbar. -Remark: If you are familiar with the MITK Workbench, you know that clicking and moving the mouse in any of the 2D render windows will move around the crosshair that defines what part of the image is displayed. -This behavior is disabled whilst any of the manual segmentation tools are active- otherwise you might have a hard time concentrating on the contour you are drawing. +Remark: Clicking and moving the mouse in any of the 2D render windows will move the crosshair that defines what part of the image is displayed. +This behavior is disabled as long as any of the manual segmentation tools are active - otherwise you might have a hard time concentrating on the contour you are drawing. - -\subsection org_mitk_views_segmentationUserManualAddSubtractTools Add and Subtract Tools -\imageMacro{QmitkSegmentation_IMGIconAddSubtract.png,"Add and Subtract Tools",7.70} +\subsection org_mitk_views_segmentationaddsubtracttools Add and subtract tools +\imageMacro{QmitkSegmentation_IMGIconAddSubtract.png,"Add and subtract tools",7.70} Use the left mouse button to draw a closed contour. When releasing the mouse button, the contour will be added (Add tool) to or removed (Subtract tool) from the current segmentation. Adding and subtracting voxels can be iteratively repeated for the same segmentation. Holding CTRL / CMD while drawing will invert the current tool's behavior (i.e. instead of adding voxels, they will be subtracted). -\subsection org_mitk_views_segmentationUserManualPaintWipeTools Paint and Wipe Tools -\imageMacro{QmitkSegmentation_IMGIconPaintWipe.png,"Paint and Wipe Tools",7.68} +\subsection org_mitk_views_segmentationpaintwipetools Paint and wipe tools +\imageMacro{QmitkSegmentation_IMGIconPaintWipe.png,"Paint and wipe tools",7.68} Use the Size slider to change the radius of the round paintbrush tool. Move the mouse in any 2D window and press the left button to draw or erase pixels. Holding CTRL / CMD while drawing will invert the current tool's behavior (i.e. instead of painting voxels, they will be wiped). -\subsection org_mitk_views_segmentationUserManualRegionGrowingTool Region Growing Tool -\imageMacro{QmitkSegmentation_IMGIconRegionGrowing.png,"Region Growing Tool",3.81} +\subsection org_mitk_views_segmentationregiongrowingtool Region growing tool +\imageMacro{QmitkSegmentation_IMGIconRegionGrowing.png,"Region growing tool",3.81} Click at one point in a 2D slice widget to add an image region to the segmentation with the region growing tool. -Region Growing selects all pixels around the mouse cursor that have a similar gray value as the pixel below the mouse cursor. This enables you to quickly create segmentations of structures that have a good contrast to surrounding tissue. +Region Growing selects all pixels around the mouse cursor that have a similar gray value as the pixel below the mouse cursor. +This allows to quickly create segmentations of structures that have a good contrast to surrounding tissue. The tool operates based on the current level window, so changing the level window to optimize the contrast for the ROI is encouraged. -Moving the mouse up/down is different from left/right: Moving up the cursor while holding the left mouse button widens the range for the included grey values; moving it down narrows it. Moving the mouse left and right will shift the range. +Moving the mouse up / down is different from left / right: +Moving up the cursor while holding the left mouse button widens the range for the included grey values; moving it down narrows it. +Moving the mouse left and right will shift the range. The tool will select more or less pixels, corresponding to the changing gray value range. - - \if THISISNOTIMPLEMENTEDATTHEMOMENT A common issue with region growing is the so called "leakage" which happens when the structure of interest is connected to other pixels, of similar gray values, through a narrow "bridge" at the border of the structure. The Region Growing tool comes with a "leakage detection/removal" feature. If leakage happens, you can left-click into the leakage region and the tool will try to automatically remove this region (see illustration below). -\imageMacro{QmitkSegmentation_IMGLeakage.png,"Leakage correction feature of the Region Growing tool",11.28} +\imageMacro{QmitkSegmentation_Leakage.png,"Leakage correction feature of the region growing tool",11.28} \endif - - -\subsection org_mitk_views_segmentationUserManualFillTool Fill Tool -\imageMacro{QmitkSegmentation_IMGIconFill.png,"Fill Tool",3.81} +\subsection org_mitk_views_segmentationfilltool Fill tool +\imageMacro{QmitkSegmentation_IMGIconFill.png,"Fill tool",3.81} Left-click inside a segmentation with holes to completely fill all holes. Left-click inside a hole to fill only this specific hole. -\subsection org_mitk_views_segmentationUserManualEraseTool Erase Tool +\subsection org_mitk_views_segmentationerasetool Erase tool -\imageMacro{QmitkSegmentation_IMGIconErase.png,"Erase Tool",3.79} +\imageMacro{QmitkSegmentation_IMGIconErase.png,"Erase tool",3.79} -This tool removes a connected part of pixels that form a segmentation. You may use it to remove single segmentations (left-click on specific segmentation) or to clear a whole slice at once (left-click outside a segmentation). +This tool removes a connected part of pixels that form a segmentation. +You may use it to remove single segmentations (left-click on specific segmentation) or to clear a whole slice at once (left-click outside a segmentation). -\subsection org_mitk_views_segmentationUserManualLiveWireTool Live Wire Tool +\subsection org_mitk_views_segmentationlivewiretool Live wire tool -\imageMacro{QmitkSegmentation_IMGIconLiveWire.png,"Live Wire Tool",3.01} +\imageMacro{QmitkSegmentation_IMGIconLiveWire.png,"Live wire tool",3.01} The Live Wire Tool acts as a magnetic lasso with a contour snapping to edges of objects. \imageMacro{QmitkSegmentation_IMGLiveWireUsage.PNG,"Steps for using the Live Wire Tool",16.00}
    -
  • (1) To start the tool you have to double-click near the edge of the object you want to segment. The initial anchor point will snap to the edge within a 3x3 region. -
  • (2) Move the mouse. You don't have trace the edge of the object. The contour will automatically snap to it. -
  • (3) To fix a segment you can set anchor points by single left mouse button click. -
  • (4) Go on with moving the mouse and setting anchor points. -
  • (5) To close the contour double-click on the initial anchor point. -
  • (6) After closing, the contour can be edited by moving, inserting and deleting anchor points. +
  • (1) To start the tool you have to double-click near the edge of the object you want to segment. The initial anchor point will snap to the edge within a 3x3 region. +
  • (2) Move the mouse. You don't have trace the edge of the object. The contour will automatically snap to it. +
  • (3) To fix a segment you can set anchor points by single left mouse button click. +
  • (4) Go on with moving the mouse and setting anchor points. +
  • (5) To close the contour double-click on the initial anchor point. +
  • (6) After closing, the contour can be edited by moving, inserting and deleting anchor points.
The contour will be transferred to its binary image representation by deactivating the tool. -\subsection org_mitk_views_segmentationUserManualManualKringeling5 2D and 3D Interpolation +\subsection org_mitk_views_segmentationinterpolation 2D and 3D Interpolation Creating segmentations using 2D manual contouring for large image volumes may be very time-consuming, because structures of interest may cover a large range of slices. +Note: Interpolation is currently disabled for segmentations containing more than one label. The segmentation view offers two helpful features to mitigate this drawback: +
    -
  • 2D Interpolation -
  • 3D Interpolation +
  • 2D Interpolation +
  • 3D Interpolation
The 2D Interpolation creates suggestions for a segmentation whenever you have a slice that +
    -
  • has got neighboring slices with segmentations (these do not need to be direct neighbors but could also be a couple of slices away) AND -
  • is completely clear of a manual segmentation, i.e. there will be no suggestion if there is even only a single pixel of segmentation in the current slice. +
  • has got neighboring slices with segmentations (these do not need to be direct neighbors but could also be a couple of slices away) AND +
  • is completely clear of a manual segmentation, i.e. there will be no suggestion if there is even only a single pixel of segmentation in the current slice.
-\imageMacro{QmitkSegmentation_2DInterpolation.png,"2D Interpolation Usage",3.01} +\imageMacro{QmitkSegmentation_2DInterpolation.png,"2D interpolation usage",3.01} Interpolated suggestions are displayed as outlines, until you confirm them as part of the segmentation. To confirm single slices, click the Confirm for single slice button below the toolbox. You may also review the interpolations visually and then accept all of them at once by selecting Confirm for all slices. The 3D interpolation creates suggestions for 3D segmentations. That means if you start contouring, from the second contour onwards, the surface of the segmented area will be interpolated based on the given contour information. The interpolation works with all available manual tools. Please note that this is currently a pure mathematical interpolation, i.e. image intensity information is not taken into account. With each further contour the interpolation result will be improved, but the more contours you provide the longer the recalculation will take. To achieve an optimal interpolation result and in this way a most accurate segmentation you should try to describe the surface with sparse contours by segmenting in arbitrary oriented planes. The 3D interpolation is not meant to be used for parallel slice-wise segmentation, but rather segmentations in i.e. the axial, coronal and sagittal plane. -\imageMacro{QmitkSegmentation_3DInterpolationWrongRight.png,"3D Interpolation Usage",16.00} +\imageMacro{QmitkSegmentation_3DInterpolationWrongRight.png,"3D interpolation usage",16.00} You can accept the interpolation result by clicking the Confirm-button below the tool buttons. In this case the 3D interpolation will be deactivated automatically so that the result can be post-processed without any interpolation running in the background. Additional to the surface, black contours are shown in the 3D render window, which mark all the drawn contours used for the interpolation. You can navigate between the drawn contours by clicking on the corresponding position nodes in the data manager which are stored as sub-nodes of the selected segmentation. If you do not want to see these nodes just uncheck the Show Position Nodes checkbox and these nodes will be hidden. -If you want to delete a drawn contour we recommend to use the Erase-Tool since Redo/Undo is not yet working for 3D interpolation. +If you want to delete a drawn contour we recommend to use the Erase-Tool since undo / redo is not yet working for 3D interpolation. The current state of the 3D interpolation can be saved across application restart. For that, just click on save project during the interpolation is active. After restarting the application and load your project you can click on "Reinit Interpolation" within the 3D interpolation GUI area. - -\section org_mitk_views_segmentationUserManual3DSegmentationTools 3D Segmentation Tools +\section org_mitk_views_segmentation3dsegmentation 3D segmentation tools The 3D tools operate on the whole image and require usually a small amount of interaction like placing seed-points or specifying certain parameters. All 3D tools provide an immediate segmentation feedback, which is displayed as a transparent green overlay. For accepting a preview you have to press the Confirm button of the selected tool. The following 3D tools are available: -\subsection org_mitk_views_segmentationUserManual3DThresholdTool 3D Threshold Tool +\subsection org_mitk_views_segmentation3dthresholdtool 3D Threshold tool The thresholding tool simply applies a 3D threshold to the patient image. All pixels with values equal or above the selected threshold are labeled as part of the segmentation. You can change the threshold by either moving the slider of setting a certain value in the spinbox. \imageMacro{QmitkSegmentation_3DThresholdTool.png,"3D Threshold tool",10.00} -\subsection org_mitk_views_segmentationUserManual3DULTool 3D Upper/Lower Threshold Tool +\subsection org_mitk_views_segmentation3dulthresholdTool 3D upper / lower threshold tool The Upper/Lower Thresholding tool works similar to the simple 3D threshold tool but allows you to define an upper and lower threshold. All pixels with values within this threshold interval will be labeled as part of the segmentation. -\imageMacro{QmitkSegmentation_3DULThresholdTool.png,"3D Upper/Lower Threshold tool",10.00} +\imageMacro{QmitkSegmentation_3DULThresholdTool.png,"3D upper / lower threshold tool",10.00} -\subsection org_mitk_views_segmentationUserManual3DOtsuTool 3D Otsu Tool +\subsection org_mitk_views_segmentation3dotsutool 3D Otsu tool The 3D Otsu tool provides a more sophisticated thresholding algorithm. It allows you to define a number of regions. Based on the image histogram the pixels will then be divided into different regions. The more regions you define the longer the calculation will take. You can select afterwards which of these regions you want to confirm as segmentation. \imageMacro{QmitkSegmentation_3DOtsuTool.png,"3D Otsu tool",10.00} -\subsection org_mitk_views_segmentationUserManual3DRGTool 3D Region Growing Tool +\subsection org_mitk_views_segmentation3drgtool 3D Region growing tool The 3D Region Growing tool works similar to the 2D pendant. At the beginning you have to place a seedpoint and define a threshold interval. If you press Run Segmentation a preview is calculated. By moving the Adapt region growing slider you can interactively adapt the segmentation result. -\imageMacro{QmitkSegmentation_3DRGTool.png,"3D Region Growing tool",10.00} +\imageMacro{QmitkSegmentation_3DRGTool.png,"3D Region growing tool",10.00} -\subsection org_mitk_views_segmentationUserManualPickingTool Picking Tool +\subsection org_mitk_views_segmentationpickingtool Picking Tool The Picking tool allows you to select islands within your segmentation. This is especially useful if e.g. a thresholding provided you with several areas within your image but you are just interested in one special region. \imageMacro{QmitkSegmentation_PickingTool.png,"Picking tool",10.00} -\subsection org_mitk_views_segmentationUserManualnnUNetTool nnU-Net Tool (Ubuntu only) +\subsection org_mitk_views_segmentationnnUNetTool nnU-Net Tool (Ubuntu only) \imageMacro{QmitkSegmentation_nnUnetTool.png,"nnUNet tool",10.00} This tool provides a GUI to the deep learning-based segmentation algorithm called the nnUNet. With this tool, you can get a segmentation mask predicted for the loaded image in MITK. Be ready with the pre-trained weights (a.k.a RESULTS_FOLDER) for your organ or task concerned, before using the tool. For a detailed explanation of the parameters and pre-trained weights folder structure etc., please refer to https://github.com/MIC-DKFZ/nnUNet.
Remark: The tool assumes that you have a Python3 environment with nnUNet (pip) installed. Your machine should be also equipped with a CUDA enabled GPU. -\subsubsection org_mitk_views_segmentationUserManualnnUNetToolWorkflow Workflow: - -# Click on the "nnUNet Results Folder" directory icon and navigate to the results folder on your hard disk. This is equivalent to setting the RESULTS_FOLDER environment variable. If your results folder is as - per the nnUNet required folder structure, the configuration, trainers, tasks and folds are automatically parsed and correspondingly loaded in the drop-down boxes as shown below. +\subsubsection org_mitk_views_segmentationnnUNetToolWorkflow Workflow: + -# Select the "Python Path" drop-down to see if MITK has automatically detected other Python environments. + Click on a fitting environment for the nnUNet inference or click "Select" in the dropdown to choose an unlisted python environment. Note that, while selecting an arbitrary environment folder, only select the base folder, e.g. "myenv". + No need to select all the way until "../myenv/bin/python", for example. + -# Click on the "nnUNet Results Folder" directory icon to navigate to the results folder on your hard disk. This is equivalent to setting the RESULTS_FOLDER environment variable. If your results folder is as + per the nnUNet required folder structure, the configuration, trainers, tasks and folds are automatically parsed and correspondingly loaded in the drop-down boxes as shown below. Note that MITK automatically checks for the + RESULTS_FOLDER environment variable value and, if found, auto parses that directory when the tool is started. \imageMacro{QmitkSegmentation_nnUNet_Settings.png,"nnUNet Segmentation Settings",10} - -# Choose your required configuration-task-trainer-fold parameters, sequentially. By default, all entries are selected inside the "Fold" dropdown (shown: "All"). + -# Choose your required Task-Configuration-Trainer-Planner-Fold parameters, sequentially. By default, all entries are selected inside the "Fold" dropdown (shown: "All"). Note that, even if you uncheck all entries from the "Fold" dropdown (shown: "None"), then too, all folds would be considered for inferencing. -# For ensemble predictions, you will get the option to select parameters irrespective on postprocessing files available in the ensembles folder of RESULTS_FOLDER. Note that, if a postprocessing json file exists for the selected combination then it will used for ensembling, by default. To choose not to, uncheck the "Use PostProcessing JSON" in the "Advanced" section. \imageMacro{QmitkSegmentation_nnUNet_ensemble.png,"nnUNet Segmentation Settings",10} - -# If your task requires multi-modal inputs, then check the "Multi-Modal" checkbox and enter the no.of modalities additionally required for inferencing, in the "No. of Extra Modalities" spinbox. - Instantly, as much node selectors should appear below to select other image nodes from the Data Manager along including a selector with preselected with the reference node. - Certain pre-trained configurations are trained in a particular modal order. Please be aware of the order beforehand & select the image nodes in the node selectors accordingly for accurate inferencing. + -# If your task is trained with multi-modal inputs, then "Multi-Modal" checkbox is checked and the no.of modalities are preloaded in the "No. of Extra Modalities" spinbox. + Instantly, as much node selectors with corresponding modality names should appear below to select the Data Manager along including a selector with preselected with the reference node. + Now, select the image nodes in the node selectors accordingly for accurate inferencing. \imageMacro{QmitkSegmentation_nnUNet_multimodal.png,"nnUNet Multi Modal Settings",10.00} - -# Select the "Python Path" drop-down to see if MITK has automatically detected other Python environments. - Click on a fitting environment for the nnUNet inference or click "Select" in the dropdown to choose an unlisted python environment. Note that, while selecting an arbitrary environment folder, only select the base folder. - No need to select all the way until "../myenv/bin/python", for example. -# Click on "Preview". -# In the "Advanced" section, you can also activate other options like "Mixed Precision" and "Enable Mirroring" (for test time data augmentation) pertaining to nnUNet. \imageMacro{QmitkSegmentation_nnUNet_Advanced.png,"nnUNet Advanced Settings",10.00} - -# Use "GPU Id" to change the preferred GPU for inferencing. This is equivalent to setting the CUDA_VISIBLE_DEVICES environment variable. + -# Use "Advanced" > "GPU Id" combobox to change the preferred GPU for inferencing. This is internally equivalent to setting the CUDA_VISIBLE_DEVICES environment variable. -# Every inferred segmentation is cached to prevent a redundant computation. In case, a user doesn't wish to cache a Preview, uncheck the "Enable Caching" in the "Advanced" section. This will ensure that the - current parameters will neither be checked against the existing cache nor a segmentation be loaded from it. + current parameters will neither be checked against the existing cache nor a segmentation be loaded from it when Preview is clicked. -# You may always clear all the cached segmentations by clicking "Clear Cache" button. -\subsubsection org_mitk_views_segmentationUserManualnnUNetToolMisc Miscellaneous: +\subsubsection org_mitk_views_segmentationnnUNetToolMisc Miscellaneous: -# In case you want to reload/reparse the folders in the "nnUNet Results Folder", eg. after adding new tasks into it, you may do so without reselecting the folder again by clicking the "Refresh Results Folder" button. - -# If you have a nnUNet code-base yet to be pip-installed into any environment (eg. developer modified nnUNet forks), click checkbox "No pip". - Once the checkbox is checked a new directory selector appears in the section. You may click and navigate to your nnUNet fork folder to select the code from which you want inference. - \imageMacro{QmitkSegmentation_nnUNet_nopip.png,"nnUNet Advanced Settings with option to select nnUNet local folder",10.00} -# The "Advanced" > "GPU Id" combobox lists the Nvidia GPUs available by parsing the nvidia-smi utility output. In case your machine has Nvidia CUDA enabled GPUs but the nvidia-smi fails for some reason, the "GPU Id" combobox will show no entries. In such a situation, it's still possible to execute inferencing by manually entering the preferred GPU Id, eg. 0 in the combobox. + -# In the RESULTS_FOLDER directory, inside the trainer-planner folder of every task, MITK keeps a "mitk_export.json" file for fast loading for multi-modal information. It is recommended not to delete this file(s) for a fast responsive UI. + Tip: If multi-modal information shown on MITK is not correct for a given task, you may modify this JSON file and try again. - -\section org_mitk_views_segmentationUserManualPostprocessing Additional things you can do with segmentations +\section org_mitk_views_segmentationpostprocessing Additional things you can do with segmentations Segmentations are never an end in themselves. Consequently, the segmentation view adds a couple of "post-processing" actions, accessible through the context-menu of the data manager. -\imageMacro{QmitkSegmentation_IMGDataManagerContextMenu.png,"Context menu items for segmentations.",10.58} +\imageMacro{QmitkSegmentation_IMGDataManagerContextMenu.png,"Context menu items for segmentations",10.58}
  • Create polygon %model applies the marching cubes algorithm to the segmentation. This polygon %model can be used for visualization in 3D or other applications such as stereolithography (3D printing).
  • Create smoothed polygon %model uses smoothing in addition to the marching cubes algorithm, which creates models that do not follow the exact outlines of the segmentation, but look smoother.
  • Autocrop can save memory. Manual segmentations have the same extent as the patient image, even if the segmentation comprises only a small sub-volume. This invisible and meaningless margin is removed by autocropping.
- -\section org_mitk_views_segmentationof3DTImages Segmentation of 3D+t images +\section org_mitk_views_segmentationof3dtimages Segmentation of 3D+t images For segmentation of 3D+t images, some tools give you the option to choose between creating dynamic and static masks.
    -
  • Dynamic masks can be created on each time frame individually. -
  • Static masks will be defined on one time frame and will be the same for all other time frames. +
  • Dynamic masks can be created on each time frame individually. +
  • Static masks will be defined on one time frame and will be the same for all other time frames.
-In general, segmentation is applied on the time frame that is selected when execution is performed. If you alter the time frame, the segmentation preview is adapted. - +In general, segmentation is applied on the time frame that is selected when execution is performed. +If you alter the time frame, the segmentation preview is adapted. -\section org_mitk_views_segmentationUserManualTechnicalDetail Technical Information for Developers +\section org_mitk_views_segmentationtechnicaldetail Technical information for developers -For technical specifications see \subpage QmitkSegmentationTechnicalPage and for information on the extensions of the tools system \subpage toolextensions . +For technical specifications see \subpage QmitkSegmentationTechnicalPage and for information on the extensions of the tools system \subpage toolextensions. */ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationPlugin_Overview.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationPlugin_Overview.png index b4c6f19548..8af46ac815 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationPlugin_Overview.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationPlugin_Overview.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationPreferences.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationPreferences.png index f86a955ee4..07710074cb 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationPreferences.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentationPreferences.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_3DWatershedTool.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_3DWatershedTool.png deleted file mode 100644 index a82c1aa1b3..0000000000 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_3DWatershedTool.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGtoolbar.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_DataSelection.png similarity index 100% rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGtoolbar.png rename to Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_DataSelection.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_IMG2DFastMarchingUsage.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_IMG2DFastMarchingUsage.png deleted file mode 100644 index 9f1ac4f3b3..0000000000 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_IMG2DFastMarchingUsage.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGlabeltable.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_LabelTable.png similarity index 100% rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGlabeltable.png rename to Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_LabelTable.png diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGlayerManager.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_LayerSelection.png similarity index 100% rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGlayerManager.png rename to Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_LayerSelection.png diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGLabelTableMultipleSelectionContextMenu.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_OperationsMultiSelection.png similarity index 100% rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGLabelTableMultipleSelectionContextMenu.png rename to Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_OperationsMultiSelection.png diff --git a/Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGLabelTableSingleSelectionContextMenu.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_OperationsSingleSelection.png similarity index 100% rename from Plugins/org.mitk.gui.qt.multilabelsegmentation/documentation/UserManual/org_mitk_views_multilabelsegmentationIMGLabelTableSingleSelectionContextMenu.png rename to Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_OperationsSingleSelection.png diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_ToolOverview.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_ToolOverview.png index 69d020b069..9fcbcc67f7 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_ToolOverview.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_ToolOverview.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_Advanced.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_Advanced.png index 3b23a185d5..22c26d4852 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_Advanced.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_Advanced.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_Settings.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_Settings.png index 6f7f9b376b..0ac99f74c2 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_Settings.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_Settings.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_ensemble.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_ensemble.png index f2abb865da..39f858d753 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_ensemble.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_ensemble.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_multimodal.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_multimodal.png index 981b5b5593..9cdb4940a3 100644 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_multimodal.png and b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_multimodal.png differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_nopip.png b/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_nopip.png deleted file mode 100644 index ffedb985dd..0000000000 Binary files a/Plugins/org.mitk.gui.qt.segmentation/documentation/UserManual/QmitkSegmentation_nnUNet_nopip.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.segmentation/files.cmake b/Plugins/org.mitk.gui.qt.segmentation/files.cmake index f63907b3ae..bb27b67836 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/files.cmake +++ b/Plugins/org.mitk.gui.qt.segmentation/files.cmake @@ -1,80 +1,81 @@ set(SRC_CPP_FILES QmitkSegmentationPreferencePage.cpp ) set(INTERNAL_CPP_FILES mitkPluginActivator.cpp QmitkSegmentationView.cpp QmitkAutocropAction.cpp QmitkAutocropLabelSetImageAction.cpp QmitkCreatePolygonModelAction.cpp QmitkLoadMultiLabelPresetAction.cpp QmitkSaveMultiLabelPresetAction.cpp Common/QmitkDataSelectionWidget.cpp Common/QmitkLabelsWidget.cpp Common/QmitkLayersWidget.cpp SegmentationUtilities/QmitkSegmentationUtilitiesView.cpp SegmentationUtilities/QmitkSegmentationUtilityWidget.cpp SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.cpp SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.cpp SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.cpp SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.cpp SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.cpp ) set(UI_FILES + src/QmitkSegmentationPreferencePageControls.ui src/internal/QmitkSegmentationViewControls.ui src/internal/Common/QmitkDataSelectionWidgetControls.ui src/internal/Common/QmitkLabelsWidgetControls.ui src/internal/Common/QmitkLayersWidgetControls.ui src/internal/SegmentationUtilities/QmitkSegmentationUtilitiesViewControls.ui src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidgetControls.ui src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidgetControls.ui src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidgetControls.ui src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidgetControls.ui src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidgetControls.ui ) set(MOC_H_FILES src/QmitkSegmentationPreferencePage.h src/internal/mitkPluginActivator.h src/internal/QmitkSegmentationView.h src/internal/QmitkAutocropAction.h src/internal/QmitkAutocropLabelSetImageAction.h src/internal/QmitkCreatePolygonModelAction.h src/internal/QmitkLoadMultiLabelPresetAction.h src/internal/QmitkSaveMultiLabelPresetAction.h src/internal/Common/QmitkDataSelectionWidget.h src/internal/Common/QmitkLabelsWidget.h src/internal/Common/QmitkLayersWidget.h src/internal/SegmentationUtilities/QmitkSegmentationUtilitiesView.h src/internal/SegmentationUtilities/QmitkSegmentationUtilityWidget.h src/internal/SegmentationUtilities/BooleanOperations/QmitkBooleanOperationsWidget.h src/internal/SegmentationUtilities/ImageMasking/QmitkImageMaskingWidget.h src/internal/SegmentationUtilities/ContourModelToImage/QmitkContourModelToImageWidget.h src/internal/SegmentationUtilities/MorphologicalOperations/QmitkMorphologicalOperationsWidget.h src/internal/SegmentationUtilities/SurfaceToImage/QmitkSurfaceToImageWidget.h ) set(CACHED_RESOURCE_FILES resources/segmentation.svg resources/segmentation_utilities.svg plugin.xml ) set(QRC_FILES resources/segmentation.qrc resources/SegmentationUtilities.qrc resources/BooleanOperationsWidget.qrc resources/MorphologicalOperationsWidget.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.segmentation/plugin.xml b/Plugins/org.mitk.gui.qt.segmentation/plugin.xml index b0753685a8..7da109cf6a 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/plugin.xml +++ b/Plugins/org.mitk.gui.qt.segmentation/plugin.xml @@ -1,82 +1,80 @@ Allows the segmentation of images using different tools. Edit segmentations using standard operations. - - diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp index e49cce88d5..f77ebd2f21 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp @@ -1,176 +1,135 @@ /*============================================================================ 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 "QmitkSegmentationPreferencePage.h" -#include -#include -#include -#include -#include -#include -#include -#include - #include #include +#include + +#include + +#include + QmitkSegmentationPreferencePage::QmitkSegmentationPreferencePage() - : m_MainControl(nullptr), - m_SlimViewCheckBox(nullptr), - m_RadioOutline(nullptr), - m_RadioOverlay(nullptr), - m_SelectionModeCheckBox(nullptr), - m_SmoothingCheckBox(nullptr), - m_SmoothingSpinBox(nullptr), - m_DecimationSpinBox(nullptr), - m_ClosingSpinBox(nullptr), + : m_Ui(new Ui::QmitkSegmentationPreferencePageControls), + m_Control(nullptr), m_Initializing(false) { - } QmitkSegmentationPreferencePage::~QmitkSegmentationPreferencePage() { - } void QmitkSegmentationPreferencePage::Init(berry::IWorkbench::Pointer) { - } void QmitkSegmentationPreferencePage::CreateQtControl(QWidget* parent) { m_Initializing = true; berry::IPreferencesService* prefService = berry::Platform::GetPreferencesService(); m_SegmentationPreferencesNode = prefService->GetSystemPreferences()->Node("/org.mitk.views.segmentation"); - m_MainControl = new QWidget(parent); - - auto formLayout = new QFormLayout; - formLayout->setHorizontalSpacing(8); - formLayout->setVerticalSpacing(24); - - m_SlimViewCheckBox = new QCheckBox("Hide tool button texts and increase icon size", m_MainControl); - formLayout->addRow("Slim view", m_SlimViewCheckBox); - - auto displayOptionsLayout = new QVBoxLayout; - m_RadioOutline = new QRadioButton("Draw as outline", m_MainControl); - displayOptionsLayout->addWidget(m_RadioOutline); - m_RadioOverlay = new QRadioButton("Draw as transparent overlay", m_MainControl); - displayOptionsLayout->addWidget(m_RadioOverlay); - formLayout->addRow("2D display", displayOptionsLayout); - - m_SelectionModeCheckBox = new QCheckBox("Show only selected nodes", m_MainControl); - m_SelectionModeCheckBox->setToolTip("If checked the segmentation plugin ensures that only the selected segmentation" - "and the reference image are visible at one time."); - formLayout->addRow("Data node selection mode", m_SelectionModeCheckBox); - - auto surfaceLayout = new QFormLayout; - surfaceLayout->setSpacing(8); - - m_SmoothingCheckBox = new QCheckBox("Use image spacing as smoothing value hint", m_MainControl); - surfaceLayout->addRow(m_SmoothingCheckBox); - connect(m_SmoothingCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSmoothingCheckboxChecked(int))); - - m_SmoothingSpinBox = new QDoubleSpinBox(m_MainControl); - m_SmoothingSpinBox->setMinimum(0.0); - m_SmoothingSpinBox->setSingleStep(0.5); - m_SmoothingSpinBox->setValue(1.0); - m_SmoothingSpinBox->setToolTip("The Smoothing value is used as variance for a gaussian blur."); - surfaceLayout->addRow("Smoothing value (mm)", m_SmoothingSpinBox); - - m_DecimationSpinBox = new QDoubleSpinBox(m_MainControl); - m_DecimationSpinBox->setMinimum(0.0); - m_DecimationSpinBox->setMaximum(0.99); - m_DecimationSpinBox->setSingleStep(0.1); - m_DecimationSpinBox->setValue(0.5); - m_DecimationSpinBox->setToolTip("Valid range is [0, 1). High values increase decimation, especially when very close " - "to 1. A value of 0 disables decimation."); - surfaceLayout->addRow("Decimation rate", m_DecimationSpinBox); - - m_ClosingSpinBox = new QDoubleSpinBox(m_MainControl); - m_ClosingSpinBox->setMinimum(0.0); - m_ClosingSpinBox->setMaximum(1.0); - m_ClosingSpinBox->setSingleStep(0.1); - m_ClosingSpinBox->setValue(0.0); - m_ClosingSpinBox->setToolTip("Valid range is [0, 1]. Higher values increase closing. A value of 0 disables closing."); - surfaceLayout->addRow("Closing Ratio", m_ClosingSpinBox); - - formLayout->addRow("Smoothed surface creation", surfaceLayout); - - m_MainControl->setLayout(formLayout); + m_Control = new QWidget(parent); + m_Ui->setupUi(m_Control); + + connect(m_Ui->smoothingCheckBox, SIGNAL(stateChanged(int)), this, SLOT(OnSmoothingCheckboxChecked(int))); + connect(m_Ui->labelSetPresetToolButton, SIGNAL(clicked()), this, SLOT(OnLabelSetPresetButtonClicked())); + this->Update(); m_Initializing = false; } QWidget* QmitkSegmentationPreferencePage::GetQtControl() const { - return m_MainControl; + return m_Control; } bool QmitkSegmentationPreferencePage::PerformOk() { - m_SegmentationPreferencesNode->PutBool("slim view", m_SlimViewCheckBox->isChecked()); - m_SegmentationPreferencesNode->PutBool("draw outline", m_RadioOutline->isChecked()); - m_SegmentationPreferencesNode->PutBool("selection mode", m_SelectionModeCheckBox->isChecked()); - m_SegmentationPreferencesNode->PutBool("smoothing hint", m_SmoothingCheckBox->isChecked()); - m_SegmentationPreferencesNode->PutDouble("smoothing value", m_SmoothingSpinBox->value()); - m_SegmentationPreferencesNode->PutDouble("decimation rate", m_DecimationSpinBox->value()); - m_SegmentationPreferencesNode->PutDouble("closing ratio", m_ClosingSpinBox->value()); + m_SegmentationPreferencesNode->PutBool("slim view", m_Ui->slimViewCheckBox->isChecked()); + m_SegmentationPreferencesNode->PutBool("draw outline", m_Ui->outlineRadioButton->isChecked()); + m_SegmentationPreferencesNode->PutBool("selection mode", m_Ui->selectionModeCheckBox->isChecked()); + m_SegmentationPreferencesNode->PutBool("smoothing hint", m_Ui->smoothingCheckBox->isChecked()); + m_SegmentationPreferencesNode->PutDouble("smoothing value", m_Ui->smoothingSpinBox->value()); + m_SegmentationPreferencesNode->PutDouble("decimation rate", m_Ui->decimationSpinBox->value()); + m_SegmentationPreferencesNode->PutDouble("closing ratio", m_Ui->closingSpinBox->value()); + m_SegmentationPreferencesNode->Put("label set preset", m_Ui->labelSetPresetLineEdit->text()); return true; } void QmitkSegmentationPreferencePage::PerformCancel() { } void QmitkSegmentationPreferencePage::Update() { - m_SlimViewCheckBox->setChecked(m_SegmentationPreferencesNode->GetBool("slim view", false)); + m_Ui->slimViewCheckBox->setChecked(m_SegmentationPreferencesNode->GetBool("slim view", false)); if (m_SegmentationPreferencesNode->GetBool("draw outline", true)) { - m_RadioOutline->setChecked(true); + m_Ui->outlineRadioButton->setChecked(true); } else { - m_RadioOverlay->setChecked(true); + m_Ui->overlayRadioButton->setChecked(true); } - m_SelectionModeCheckBox->setChecked(m_SegmentationPreferencesNode->GetBool("selection mode", false)); + m_Ui->selectionModeCheckBox->setChecked(m_SegmentationPreferencesNode->GetBool("selection mode", false)); if (m_SegmentationPreferencesNode->GetBool("smoothing hint", true)) { - m_SmoothingCheckBox->setChecked(true); - m_SmoothingSpinBox->setDisabled(true); + m_Ui->smoothingCheckBox->setChecked(true); + m_Ui->smoothingSpinBox->setDisabled(true); } else { - m_SmoothingCheckBox->setChecked(false); - m_SmoothingSpinBox->setEnabled(true); + m_Ui->smoothingCheckBox->setChecked(false); + m_Ui->smoothingSpinBox->setEnabled(true); } - m_SmoothingSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("smoothing value", 1.0)); - m_DecimationSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("decimation rate", 0.5)); - m_ClosingSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("closing ratio", 0.0)); + m_Ui->smoothingSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("smoothing value", 1.0)); + m_Ui->decimationSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("decimation rate", 0.5)); + m_Ui->closingSpinBox->setValue(m_SegmentationPreferencesNode->GetDouble("closing ratio", 0.0)); + + auto labelSetPreset = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), ""); + bool isOverridenByCmdLineArg = !labelSetPreset.empty(); + + if (!isOverridenByCmdLineArg) + labelSetPreset = m_SegmentationPreferencesNode->Get("label set preset", "").toStdString(); + + m_Ui->labelSetPresetLineEdit->setDisabled(isOverridenByCmdLineArg); + m_Ui->labelSetPresetToolButton->setDisabled(isOverridenByCmdLineArg); + m_Ui->labelSetPresetCmdLineArgLabel->setVisible(isOverridenByCmdLineArg); + + m_Ui->labelSetPresetLineEdit->setText(QString::fromStdString(labelSetPreset)); } void QmitkSegmentationPreferencePage::OnSmoothingCheckboxChecked(int state) { if (state != Qt::Unchecked) - m_SmoothingSpinBox->setDisabled(true); + m_Ui->smoothingSpinBox->setDisabled(true); else - m_SmoothingSpinBox->setEnabled(true); + m_Ui->smoothingSpinBox->setEnabled(true); +} + +void QmitkSegmentationPreferencePage::OnLabelSetPresetButtonClicked() +{ + const auto filename = QFileDialog::getOpenFileName(m_Control, QStringLiteral("Load Label Set Preset"), QString(), QStringLiteral("Label set preset (*.lsetp)")); + + if (!filename.isEmpty()) + m_Ui->labelSetPresetLineEdit->setText(filename); } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h index 5f816a7178..fc6406faa2 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.h @@ -1,68 +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 QMITKSEGMENTATIONPREFERENCEPAGE_H #define QMITKSEGMENTATIONPREFERENCEPAGE_H #include "org_mitk_gui_qt_segmentation_Export.h" #include #include -class QCheckBox; -class QRadioButton; -class QDoubleSpinBox; +class QWidget; + +namespace Ui +{ + class QmitkSegmentationPreferencePageControls; +} class MITK_QT_SEGMENTATION QmitkSegmentationPreferencePage : public QObject, public berry::IQtPreferencePage { Q_OBJECT Q_INTERFACES(berry::IPreferencePage) public: QmitkSegmentationPreferencePage(); ~QmitkSegmentationPreferencePage() override; void Init(berry::IWorkbench::Pointer workbench) override; void CreateQtControl(QWidget* widget) override; QWidget* GetQtControl() const override; bool PerformOk() override; void PerformCancel() override; void Update() override; protected Q_SLOTS: void OnSmoothingCheckboxChecked(int); + void OnLabelSetPresetButtonClicked(); protected: - QWidget* m_MainControl; - QCheckBox* m_SlimViewCheckBox; - QRadioButton* m_RadioOutline; - QRadioButton* m_RadioOverlay; - QCheckBox* m_SelectionModeCheckBox; - QCheckBox* m_SmoothingCheckBox; - QDoubleSpinBox* m_SmoothingSpinBox; - QDoubleSpinBox* m_DecimationSpinBox; - QDoubleSpinBox* m_ClosingSpinBox; + Ui::QmitkSegmentationPreferencePageControls* m_Ui; + QWidget* m_Control; bool m_Initializing; berry::IPreferences::Pointer m_SegmentationPreferencesNode; }; #endif // QMITKSEGMENTATIONPREFERENCEPAGE_H diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui new file mode 100644 index 0000000000..7728e50e20 --- /dev/null +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui @@ -0,0 +1,205 @@ + + + QmitkSegmentationPreferencePageControls + + + + 0 + 0 + 640 + 480 + + + + Form + + + + + + Slim view + + + + + + + Hide tool button texts and increase icon size + + + + + + + 2D display + + + + + + + + + Draw as outline + + + + + + + Draw as transparent overlay + + + + + + + + + Data node selection mode + + + + + + + If checked the segmentation plugin ensures that only the selected segmentation and the reference image are visible at one time. + + + Show only selected nodes + + + + + + + Smoothed surface creation + + + + + + + 8 + + + 8 + + + + + Smoothing value (mm) + + + + + + + The Smoothing value is used as variance for a gaussian blur. + + + 0.500000000000000 + + + 1.000000000000000 + + + + + + + Decimation rate + + + + + + + Valid range is [0, 1). High values increase decimation, especially when very close to 1. A value of 0 disables decimation. + + + 0.990000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + + + Closing Ratio + + + + + + + Valid range is [0, 1]. Higher values increase closing. A value of 0 disables closing. + + + 1.000000000000000 + + + 0.100000000000000 + + + + + + + If checked the segmentation plugin ensures that only the selected segmentation and the reference image are visible at one time. + + + Use image spacing as smoothing value hint + + + + + + + + + Default label set preset + + + + + + + + + true + + + + + + + ... + + + + + + + <html><head/><body><p><span style=" color:#ff0000;">The default label set preset is currently overriden by the </span><span style=" font-family:'Courier New'; color:#ff0000;">Segmentation.labelSetPreset</span><span style=" color:#ff0000;"> command-line argument.</span></p></body></html> + + + Qt::RichText + + + true + + + + + + + + + + diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp index b013a31949..b3506ce9de 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkLoadMultiLabelPresetAction.cpp @@ -1,56 +1,56 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkLoadMultiLabelPresetAction.h" #include #include #include void QmitkLoadMultiLabelPresetAction::Run(const QList &selectedNodes) { - const auto filename = QFileDialog::getOpenFileName(nullptr, QStringLiteral("Load LabelSet Preset"), - QString(), QStringLiteral("LabelSet Preset (*.lsetp)")).toStdString(); + const auto filename = QFileDialog::getOpenFileName(nullptr, QStringLiteral("Load Label Set Preset"), + QString(), QStringLiteral("Label set preset (*.lsetp)")).toStdString(); if (filename.empty()) return; for (const auto &node : selectedNodes) { if (node.IsNull()) continue; mitk::LabelSetImage::Pointer image = dynamic_cast(node->GetData()); if (image.IsNull()) continue; mitk::LabelSetIOHelper::LoadLabelSetImagePreset(filename, image); } } void QmitkLoadMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*) { } void QmitkLoadMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*) { } void QmitkLoadMultiLabelPresetAction::SetSmoothed(bool) { } void QmitkLoadMultiLabelPresetAction::SetDecimated(bool) { } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp index b813c809e8..7b6b07ad6f 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSaveMultiLabelPresetAction.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. ============================================================================*/ #include "QmitkSaveMultiLabelPresetAction.h" #include #include #include #include void QmitkSaveMultiLabelPresetAction::Run(const QList &selectedNodes) { for (const auto &node : selectedNodes) { if (node.IsNull()) continue; mitk::LabelSetImage::Pointer image = dynamic_cast(node->GetData()); if (image.IsNull()) continue; - const auto filename = QFileDialog::getSaveFileName(nullptr, QStringLiteral("Save LabelSet Preset"), - QString(), QStringLiteral("LabelSet Preset (*.lsetp)")).toStdString(); + const auto filename = QFileDialog::getSaveFileName(nullptr, QStringLiteral("Save Label Set Preset"), + QString(), QStringLiteral("Label set preset (*.lsetp)")).toStdString(); if (filename.empty()) continue; if(!mitk::LabelSetIOHelper::SaveLabelSetImagePreset(filename, image)) { - QMessageBox::critical(nullptr, QStringLiteral("Save LabelSetImage Preset"), - QString("Could not save \"%1\" as preset.").arg(QString::fromStdString(node->GetName()))); + QMessageBox::critical(nullptr, QStringLiteral("Save Label Set Preset"), + QString("Could not save \"%1\" as label set preset.").arg(QString::fromStdString(node->GetName()))); continue; } } } void QmitkSaveMultiLabelPresetAction::SetDataStorage(mitk::DataStorage*) { } void QmitkSaveMultiLabelPresetAction::SetFunctionality(berry::QtViewPart*) { } void QmitkSaveMultiLabelPresetAction::SetSmoothed(bool) { } void QmitkSaveMultiLabelPresetAction::SetDecimated(bool) { } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp index ba0e40eef3..b6dd33bffe 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,987 +1,995 @@ /*============================================================================ 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 "QmitkSegmentationView.h" #include "mitkPluginActivator.h" // blueberry #include // mitk #include +#include #include #include #include #include +#include #include #include #include #include #include #include #include // Qmitk #include #include // us #include #include // Qt #include #include +#include #include const std::string QmitkSegmentationView::VIEW_ID = "org.mitk.views.segmentation"; QmitkSegmentationView::QmitkSegmentationView() : m_Parent(nullptr) , m_Controls(nullptr) , m_RenderWindowPart(nullptr) , m_ToolManager(nullptr) , m_ReferenceNode(nullptr) , m_WorkingNode(nullptr) , m_DrawOutline(true) , m_SelectionMode(false) , m_MouseCursorSet(false) { auto isImage = mitk::TNodePredicateDataType::New(); auto isDwi = mitk::NodePredicateDataType::New("DiffusionImage"); auto isDti = mitk::NodePredicateDataType::New("TensorImage"); auto isOdf = mitk::NodePredicateDataType::New("OdfImage"); auto isSegment = mitk::NodePredicateDataType::New("Segment"); auto validImages = mitk::NodePredicateOr::New(); validImages->AddPredicate(mitk::NodePredicateAnd::New(isImage, mitk::NodePredicateNot::New(isSegment))); validImages->AddPredicate(isDwi); validImages->AddPredicate(isDti); validImages->AddPredicate(isOdf); auto isBinary = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); auto isMask = mitk::NodePredicateAnd::New(isBinary, isImage); auto validSegmentations = mitk::NodePredicateOr::New(); validSegmentations->AddPredicate(mitk::TNodePredicateDataType::New()); validSegmentations->AddPredicate(isMask); m_SegmentationPredicate = mitk::NodePredicateAnd::New(); m_SegmentationPredicate->AddPredicate(validSegmentations); m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); m_SegmentationPredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); m_ReferencePredicate = mitk::NodePredicateAnd::New(); m_ReferencePredicate->AddPredicate(validImages); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(m_SegmentationPredicate)); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object"))); m_ReferencePredicate->AddPredicate(mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("hidden object"))); } QmitkSegmentationView::~QmitkSegmentationView() { if (nullptr != m_Controls) { OnLooseLabelSetConnection(); // deactivate all tools m_ToolManager->ActivateTool(-1); // removing all observers for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter) { (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second); } m_WorkingDataObserverTags.clear(); mitk::RenderingManager::GetInstance()->RemoveObserver(m_RenderingManagerObserverTag); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); service->RemoveAllPlanePositions(); context->ungetService(ppmRef); m_ToolManager->SetReferenceData(nullptr); m_ToolManager->SetWorkingData(nullptr); } delete m_Controls; } /**********************************************************************/ /* private Q_SLOTS */ /**********************************************************************/ void QmitkSegmentationView::OnReferenceSelectionChanged(QList nodes) { m_ToolManager->ActivateTool(-1); if (nodes.empty()) { m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate); m_ReferenceNode = nullptr; m_ToolManager->SetReferenceData(m_ReferenceNode); this->UpdateGUI(); return; } m_ReferenceNode = nodes.first(); m_ToolManager->SetReferenceData(m_ReferenceNode); if (m_ReferenceNode.IsNotNull()) { // set a predicate such that a segmentation fits the selected reference image geometry auto segPredicate = mitk::NodePredicateAnd::New(m_SegmentationPredicate.GetPointer(), mitk::NodePredicateSubGeometry::New(m_ReferenceNode->GetData()->GetGeometry())); m_Controls->workingNodeSelector->SetNodePredicate(segPredicate); if (m_SelectionMode) { // hide all image nodes to later show only the automatically selected ones mitk::DataStorage::SetOfObjects::ConstPointer imageNodes = this->GetDataStorage()->GetSubset(m_ReferencePredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = imageNodes->begin(); iter != imageNodes->end(); ++iter) { (*iter)->SetVisibility(false); } } m_ReferenceNode->SetVisibility(true); } this->UpdateGUI(); } void QmitkSegmentationView::OnSegmentationSelectionChanged(QList nodes) { m_ToolManager->ActivateTool(-1); // Remove observer if one was registered auto finding = m_WorkingDataObserverTags.find(m_WorkingNode); if (finding != m_WorkingDataObserverTags.end()) { m_WorkingNode->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[m_WorkingNode]); m_WorkingDataObserverTags.erase(m_WorkingNode); } if (nodes.empty()) { m_WorkingNode = nullptr; m_ToolManager->SetWorkingData(m_WorkingNode); this->UpdateGUI(); return; } if (m_ReferenceNode.IsNull()) { this->UpdateGUI(); return; } mitk::Image::ConstPointer referenceImage = dynamic_cast(m_ReferenceNode->GetData()); if (referenceImage.IsNull()) { this->UpdateGUI(); return; } m_WorkingNode = nodes.first(); m_ToolManager->SetWorkingData(m_WorkingNode); if (m_WorkingNode.IsNotNull()) { if (m_SelectionMode) { // hide all segmentation nodes to later show only the selected ones mitk::DataStorage::SetOfObjects::ConstPointer segmentationNodes = this->GetDataStorage()->GetSubset(m_SegmentationPredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = segmentationNodes->begin(); iter != segmentationNodes->end(); ++iter) { (*iter)->SetVisibility(false); } } m_WorkingNode->SetVisibility(true); this->OnEstablishLabelSetConnection(); m_Controls->labelSetWidget->ResetAllTableWidgetItems(); auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput); m_WorkingDataObserverTags.insert(std::pair(m_WorkingNode, m_WorkingNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command))); this->InitializeRenderWindows(referenceImage->GetTimeGeometry(), mitk::RenderingManager::REQUEST_UPDATE_ALL, false); } this->UpdateGUI(); } void QmitkSegmentationView::OnVisibilityShortcutActivated() { if (m_WorkingNode.IsNull()) { return; } bool isVisible = false; m_WorkingNode->GetBoolProperty("visible", isVisible); m_WorkingNode->SetVisibility(!isVisible); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::OnLabelToggleShortcutActivated() { if (m_WorkingNode.IsNull()) { return; } auto workingImage = dynamic_cast(m_WorkingNode->GetData()); if (nullptr == workingImage) { return; } this->WaitCursorOn(); workingImage->GetActiveLabelSet()->SetNextActiveLabel(); workingImage->Modified(); this->WaitCursorOff(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::OnNewSegmentation() { m_ToolManager->ActivateTool(-1); if (m_ReferenceNode.IsNull()) { MITK_ERROR << "'Create new segmentation' button should never be clickable unless a reference image is selected."; return; } mitk::Image::ConstPointer referenceImage = dynamic_cast(m_ReferenceNode->GetData()); if (referenceImage.IsNull()) { QMessageBox::information( m_Parent, "New segmentation", "Please load and select an image before starting some action."); return; } if (referenceImage->GetDimension() <= 1) { QMessageBox::information( m_Parent, "New segmentation", "Segmentation is currently not supported for 2D images"); return; } const auto currentTimePoint = mitk::RenderingManager::GetInstance()->GetTimeNavigationController()->GetSelectedTimePoint(); unsigned int imageTimeStep = 0; if (referenceImage->GetTimeGeometry()->IsValidTimePoint(currentTimePoint)) { imageTimeStep = referenceImage->GetTimeGeometry()->TimePointToTimeStep(currentTimePoint); } auto segTemplateImage = referenceImage; if (referenceImage->GetDimension() > 3) { auto result = QMessageBox::question(m_Parent, tr("Create a static or dynamic segmentation?"), tr("The selected image has multiple time steps.\n\nDo you want to create a static " "segmentation that is identical for all time steps or do you want to create a " "dynamic segmentation to segment individual time steps?"), tr("Create static segmentation"), tr("Create dynamic segmentation"), QString(), 0, 0); if (result == 0) { auto selector = mitk::ImageTimeSelector::New(); selector->SetInput(referenceImage); selector->SetTimeNr(0); selector->Update(); const auto refTimeGeometry = referenceImage->GetTimeGeometry(); auto newTimeGeometry = mitk::ProportionalTimeGeometry::New(); newTimeGeometry->SetFirstTimePoint(refTimeGeometry->GetMinimumTimePoint()); newTimeGeometry->SetStepDuration(refTimeGeometry->GetMaximumTimePoint() - refTimeGeometry->GetMinimumTimePoint()); mitk::Image::Pointer newImage = selector->GetOutput(); newTimeGeometry->SetTimeStepGeometry(referenceImage->GetGeometry(imageTimeStep), 0); newImage->SetTimeGeometry(newTimeGeometry); segTemplateImage = newImage; } } mitk::DataNode::Pointer newSegmentationNode; try { this->WaitCursorOn(); newSegmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(m_ReferenceNode, segTemplateImage); this->WaitCursorOff(); } catch (mitk::Exception& e) { this->WaitCursorOff(); MITK_ERROR << "Exception caught: " << e.GetDescription(); QMessageBox::warning(m_Parent, "New segmentation", "Could not create a new segmentation."); return; } auto newLabelSetImage = dynamic_cast(newSegmentationNode->GetData()); if (nullptr == newLabelSetImage) { // something went wrong return; } - mitk::Label::Pointer newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage); - newLabelSetImage->GetActiveLabelSet()->AddLabel(newLabel); + const auto labelSetPreset = this->GetDefaultLabelSetPreset(); + + if (labelSetPreset.empty() || !mitk::LabelSetIOHelper::LoadLabelSetImagePreset(labelSetPreset, newLabelSetImage)) + { + mitk::Label::Pointer newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage); + newLabelSetImage->GetActiveLabelSet()->AddLabel(newLabel); + } if (!this->GetDataStorage()->Exists(newSegmentationNode)) { this->GetDataStorage()->Add(newSegmentationNode, m_ReferenceNode); } if (m_ToolManager->GetWorkingData(0)) { m_ToolManager->GetWorkingData(0)->SetSelected(false); } newSegmentationNode->SetSelected(true); m_Controls->workingNodeSelector->SetCurrentSelectedNode(newSegmentationNode); } +std::string QmitkSegmentationView::GetDefaultLabelSetPreset() const +{ + auto labelSetPreset = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), ""); + + if (labelSetPreset.empty()) + labelSetPreset = m_LabelSetPresetPreference.toStdString(); + + return labelSetPreset; +} + void QmitkSegmentationView::OnManualTool2DSelected(int id) { this->ResetMouseCursor(); mitk::StatusBar::GetInstance()->DisplayText(""); if (id >= 0) { std::string text = "Active Tool: \""; text += m_ToolManager->GetToolById(id)->GetName(); text += "\""; mitk::StatusBar::GetInstance()->DisplayText(text.c_str()); us::ModuleResource resource = m_ToolManager->GetToolById(id)->GetCursorIconResource(); this->SetMouseCursor(resource, 0, 0); } } void QmitkSegmentationView::OnShowMarkerNodes(bool state) { mitk::SegTool2D::Pointer manualSegmentationTool; unsigned int numberOfExistingTools = m_ToolManager->GetTools().size(); for (unsigned int i = 0; i < numberOfExistingTools; i++) { manualSegmentationTool = dynamic_cast(m_ToolManager->GetToolById(i)); if (nullptr == manualSegmentationTool) { continue; } manualSegmentationTool->SetShowMarkerNodes(state); } } void QmitkSegmentationView::OnLayersChanged() { m_Controls->labelSetWidget->ResetAllTableWidgetItems(); } void QmitkSegmentationView::OnLabelsChanged() { m_Controls->labelSetWidget->ResetAllTableWidgetItems(); } void QmitkSegmentationView::OnShowLabelTable(bool value) { m_Controls->labelSetWidget->setVisible(value); } void QmitkSegmentationView::OnGoToLabel(const mitk::Point3D& pos) { if (m_RenderWindowPart) { m_RenderWindowPart->SetSelectedPosition(pos); } } void QmitkSegmentationView::OnLabelSetWidgetReset() { - this->UpdateInterpolatorWidget(); + this->ValidateSelectionInput(); } /**********************************************************************/ /* private */ /**********************************************************************/ void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { m_Parent = parent; m_Controls = new Ui::QmitkSegmentationViewControls; m_Controls->setupUi(parent); // *------------------------ // * SHORTCUTS // *------------------------ QShortcut* visibilityShortcut = new QShortcut(QKeySequence("CTRL+H"), parent); connect(visibilityShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnVisibilityShortcutActivated); QShortcut* labelToggleShortcut = new QShortcut(QKeySequence("CTRL+L"), parent); connect(labelToggleShortcut, &QShortcut::activated, this, &QmitkSegmentationView::OnLabelToggleShortcutActivated); // *------------------------ // * DATA SELECTION WIDGETS // *------------------------ m_Controls->referenceNodeSelector->SetDataStorage(GetDataStorage()); m_Controls->referenceNodeSelector->SetNodePredicate(m_ReferencePredicate); m_Controls->referenceNodeSelector->SetInvalidInfo("Select an image"); m_Controls->referenceNodeSelector->SetPopUpTitel("Select an image"); m_Controls->referenceNodeSelector->SetPopUpHint("Select an image that should be used to define the geometry and bounds of the segmentation."); m_Controls->workingNodeSelector->SetDataStorage(GetDataStorage()); m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate); m_Controls->workingNodeSelector->SetInvalidInfo("Select a segmentation"); m_Controls->workingNodeSelector->SetPopUpTitel("Select a segmentation"); m_Controls->workingNodeSelector->SetPopUpHint("Select a segmentation that should be modified. Only segmentation with the same geometry and within the bounds of the reference image are selected."); connect(m_Controls->referenceNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnReferenceSelectionChanged); connect(m_Controls->workingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkSegmentationView::OnSegmentationSelectionChanged); // *------------------------ // * TOOLMANAGER // *------------------------ m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_ToolManager->SetDataStorage(*(this->GetDataStorage())); m_ToolManager->InitializeTools(); QString segTools2D = tr("Add Subtract Fill Erase Paint Wipe 'Region Growing' 'Live Wire'"); QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' Picking"); #ifdef __linux__ segTools3D.append(" nnUNet"); // plugin not enabled for MacOS / Windows #endif std::regex extSegTool2DRegEx("SegTool2D$"); std::regex extSegTool3DRegEx("SegTool3D$"); auto tools = m_ToolManager->GetTools(); for (const auto &tool : tools) { if (std::regex_search(tool->GetNameOfClass(), extSegTool2DRegEx)) { segTools2D.append(QString(" '%1'").arg(tool->GetName())); } else if (std::regex_search(tool->GetNameOfClass(), extSegTool3DRegEx)) { segTools3D.append(QString(" '%1'").arg(tool->GetName())); } } // setup 2D tools m_Controls->toolSelectionBox2D->SetToolManager(*m_ToolManager); m_Controls->toolSelectionBox2D->SetGenerateAccelerators(true); m_Controls->toolSelectionBox2D->SetToolGUIArea(m_Controls->toolGUIArea2D); m_Controls->toolSelectionBox2D->SetDisplayedToolGroups(segTools2D.toStdString()); m_Controls->toolSelectionBox2D->SetLayoutColumns(3); m_Controls->toolSelectionBox2D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); connect(m_Controls->toolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected, this, &QmitkSegmentationView::OnManualTool2DSelected); // setup 3D Tools m_Controls->toolSelectionBox3D->SetToolManager(*m_ToolManager); m_Controls->toolSelectionBox3D->SetGenerateAccelerators(true); m_Controls->toolSelectionBox3D->SetToolGUIArea(m_Controls->toolGUIArea3D); m_Controls->toolSelectionBox3D->SetDisplayedToolGroups(segTools3D.toStdString()); m_Controls->toolSelectionBox3D->SetLayoutColumns(3); m_Controls->toolSelectionBox3D->SetEnabledMode( QmitkToolSelectionBox::EnabledWithReferenceAndWorkingDataVisible); m_Controls->slicesInterpolator->SetDataStorage(this->GetDataStorage()); // create general signal / slot connections connect(m_Controls->newSegmentationButton, &QToolButton::clicked, this, &QmitkSegmentationView::OnNewSegmentation); connect(m_Controls->slicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &QmitkSegmentationView::OnShowMarkerNodes); connect(m_Controls->layersWidget, &QmitkLayersWidget::LayersChanged, this, &QmitkSegmentationView::OnLayersChanged); connect(m_Controls->labelsWidget, &QmitkLabelsWidget::LabelsChanged, this, &QmitkSegmentationView::OnLabelsChanged); connect(m_Controls->labelsWidget, &QmitkLabelsWidget::ShowLabelTable, this, &QmitkSegmentationView::OnShowLabelTable); // *------------------------ // * LABELSETWIDGET // *------------------------ connect(m_Controls->labelSetWidget, &QmitkLabelSetWidget::goToLabel, this, &QmitkSegmentationView::OnGoToLabel); connect(m_Controls->labelSetWidget, &QmitkLabelSetWidget::LabelSetWidgetReset, this, &QmitkSegmentationView::OnLabelSetWidgetReset); m_Controls->labelSetWidget->SetDataStorage(this->GetDataStorage()); m_Controls->labelSetWidget->SetOrganColors(mitk::OrganNamesHandling::GetDefaultOrganColorString()); m_Controls->labelSetWidget->hide(); auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationView::ValidateSelectionInput); m_RenderingManagerObserverTag = mitk::RenderingManager::GetInstance()->AddObserver(mitk::RenderingManagerViewsInitializedEvent(), command); m_RenderWindowPart = this->GetRenderWindowPart(); if (nullptr != m_RenderWindowPart) { this->RenderWindowPartActivated(m_RenderWindowPart); } // Make sure the GUI notices if appropriate data is already present on creation. // Should be done last, if everything else is configured because it triggers the autoselection of data. m_Controls->referenceNodeSelector->SetAutoSelectNewNodes(true); m_Controls->workingNodeSelector->SetAutoSelectNewNodes(true); this->UpdateGUI(); } void QmitkSegmentationView::RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) { if (m_RenderWindowPart != renderWindowPart) { m_RenderWindowPart = renderWindowPart; } if (nullptr != m_Parent) { m_Parent->setEnabled(true); } if (nullptr == m_Controls) { return; } // tell the interpolation about tool manager, data storage and render window part if (nullptr != m_RenderWindowPart) { QList controllers; controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("axial")->GetSliceNavigationController()); controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("sagittal")->GetSliceNavigationController()); controllers.push_back(m_RenderWindowPart->GetQmitkRenderWindow("coronal")->GetSliceNavigationController()); m_Controls->slicesInterpolator->Initialize(m_ToolManager, controllers); } } void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_RenderWindowPart = nullptr; if (nullptr != m_Parent) { m_Parent->setEnabled(false); } } void QmitkSegmentationView::OnPreferencesChanged(const berry::IBerryPreferences* prefs) { if (nullptr != m_Controls) { bool slimView = prefs->GetBool("slim view", false); m_Controls->toolSelectionBox2D->SetShowNames(!slimView); m_Controls->toolSelectionBox3D->SetShowNames(!slimView); } m_DrawOutline = prefs->GetBool("draw outline", true); m_SelectionMode = prefs->GetBool("selection mode", false); + m_LabelSetPresetPreference = prefs->Get("label set preset", ""); + this->ApplyDisplayOptions(); } void QmitkSegmentationView::NodeAdded(const mitk::DataNode* node) { if (m_SegmentationPredicate->CheckNode(node)) { this->ApplyDisplayOptions(const_cast(node)); } } void QmitkSegmentationView::NodeRemoved(const mitk::DataNode* node) { if (!m_SegmentationPredicate->CheckNode(node)) { return; } // remove all possible contour markers of the segmentation mitk::DataStorage::SetOfObjects::ConstPointer allContourMarkers = this->GetDataStorage()->GetDerivations( node, mitk::NodePredicateProperty::New("isContourMarker", mitk::BoolProperty::New(true))); ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); for (mitk::DataStorage::SetOfObjects::ConstIterator it = allContourMarkers->Begin(); it != allContourMarkers->End(); ++it) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1; service->RemovePlanePosition(id); this->GetDataStorage()->Remove(it->Value()); } context->ungetService(ppmRef); service = nullptr; mitk::Image* image = dynamic_cast(node->GetData()); mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image); } void QmitkSegmentationView::OnEstablishLabelSetConnection() { if (m_WorkingNode.IsNull()) { return; } auto workingImage = dynamic_cast(m_WorkingNode->GetData()); if (nullptr == workingImage) { return; } workingImage->GetActiveLabelSet()->AddLabelEvent += mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->RemoveLabelEvent += mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->ModifyLabelEvent += mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent += mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->ActiveLabelEvent += mitk::MessageDelegate1(m_Controls->labelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue); // Removed in T27851 to have a chance to react to AfterChangeLayerEvent. Did it brake something? // workingImage->BeforeChangeLayerEvent += mitk::MessageDelegate( // this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); workingImage->AfterChangeLayerEvent += mitk::MessageDelegate( this, &QmitkSegmentationView::UpdateGUI); } void QmitkSegmentationView::OnLooseLabelSetConnection() { if (m_WorkingNode.IsNull()) { return; } auto workingImage = dynamic_cast(m_WorkingNode->GetData()); if (nullptr == workingImage) { return; } // Reset LabelSetWidget Events workingImage->GetActiveLabelSet()->AddLabelEvent -= mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->RemoveLabelEvent -= mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::ResetAllTableWidgetItems); workingImage->GetActiveLabelSet()->ModifyLabelEvent -= mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->AllLabelsModifiedEvent -= mitk::MessageDelegate( m_Controls->labelSetWidget, &QmitkLabelSetWidget::UpdateAllTableWidgetItems); workingImage->GetActiveLabelSet()->ActiveLabelEvent -= mitk::MessageDelegate1(m_Controls->labelSetWidget, &QmitkLabelSetWidget::SelectLabelByPixelValue); // Removed in T27851 to have a chance to react to AfterChangeLayerEvent. Did it brake something? // workingImage->BeforeChangeLayerEvent -= mitk::MessageDelegate( // this, &QmitkMultiLabelSegmentationView::OnLooseLabelSetConnection); workingImage->AfterChangeLayerEvent -= mitk::MessageDelegate( this, &QmitkSegmentationView::UpdateGUI); } void QmitkSegmentationView::ApplyDisplayOptions() { if (nullptr == m_Parent) { return; } if (nullptr == m_Controls) { return; // might happen on initialization (preferences loaded) } mitk::DataStorage::SetOfObjects::ConstPointer allImages = this->GetDataStorage()->GetSubset(m_SegmentationPredicate); for (mitk::DataStorage::SetOfObjects::const_iterator iter = allImages->begin(); iter != allImages->end(); ++iter) { this->ApplyDisplayOptions(*iter); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplyDisplayOptions(mitk::DataNode* node) { if (nullptr == node) { return; } auto labelSetImage = dynamic_cast(node->GetData()); if (nullptr != labelSetImage) { // node is a multi label segmentation // its outline property can be set in the segmentation preference page node->SetProperty("labelset.contour.active", mitk::BoolProperty::New(m_DrawOutline)); // force render window update to show outline node->GetData()->Modified(); } else if (nullptr != node->GetData()) { // node is a legacy binary segmentation bool isBinary = false; node->GetBoolProperty("binary", isBinary); if (isBinary) { node->SetProperty("outline binary", mitk::BoolProperty::New(m_DrawOutline)); node->SetProperty("outline width", mitk::FloatProperty::New(2.0)); // force render window update to show outline node->GetData()->Modified(); } } } void QmitkSegmentationView::OnContourMarkerSelected(const mitk::DataNode* node) { QmitkRenderWindow* selectedRenderWindow = nullptr; auto* renderWindowPart = this->GetRenderWindowPart(mitk::WorkbenchUtil::OPEN); auto* axialRenderWindow = renderWindowPart->GetQmitkRenderWindow("axial"); auto* sagittalRenderWindow = renderWindowPart->GetQmitkRenderWindow("sagittal"); auto* coronalRenderWindow = renderWindowPart->GetQmitkRenderWindow("coronal"); auto* threeDRenderWindow = renderWindowPart->GetQmitkRenderWindow("3d"); bool PlanarFigureInitializedWindow = false; // find initialized renderwindow if (node->GetBoolProperty("PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, axialRenderWindow->GetRenderer())) { selectedRenderWindow = axialRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, sagittalRenderWindow->GetRenderer())) { selectedRenderWindow = sagittalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, coronalRenderWindow->GetRenderer())) { selectedRenderWindow = coronalRenderWindow; } if (!selectedRenderWindow && node->GetBoolProperty( "PlanarFigureInitializedWindow", PlanarFigureInitializedWindow, threeDRenderWindow->GetRenderer())) { selectedRenderWindow = threeDRenderWindow; } // make node visible if (nullptr != selectedRenderWindow) { std::string nodeName = node->GetName(); unsigned int t = nodeName.find_last_of(" "); unsigned int id = atof(nodeName.substr(t + 1).c_str()) - 1; ctkPluginContext* context = mitk::PluginActivator::getContext(); ctkServiceReference ppmRef = context->getServiceReference(); mitk::PlanePositionManagerService* service = context->getService(ppmRef); selectedRenderWindow->GetSliceNavigationController()->ExecuteOperation(service->GetPlanePosition(id)); context->ungetService(ppmRef); selectedRenderWindow->GetRenderer()->GetCameraController()->Fit(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkSegmentationView::OnSelectionChanged(berry::IWorkbenchPart::Pointer /*part*/, const QList& nodes) { if (0 == nodes.size()) { return; } std::string markerName = "Position"; unsigned int numberOfNodes = nodes.size(); std::string nodeName = nodes.at(0)->GetName(); if ((numberOfNodes == 1) && (nodeName.find(markerName) == 0)) { this->OnContourMarkerSelected(nodes.at(0)); return; } } void QmitkSegmentationView::ResetMouseCursor() { if (m_MouseCursorSet) { mitk::ApplicationCursor::GetInstance()->PopCursor(); m_MouseCursorSet = false; } } void QmitkSegmentationView::SetMouseCursor(const us::ModuleResource& resource, int hotspotX, int hotspotY) { // Remove previously set mouse cursor if (m_MouseCursorSet) { this->ResetMouseCursor(); } if (resource) { us::ModuleResourceStream cursor(resource, std::ios::binary); mitk::ApplicationCursor::GetInstance()->PushCursor(cursor, hotspotX, hotspotY); m_MouseCursorSet = true; } } void QmitkSegmentationView::UpdateGUI() { mitk::DataNode* referenceNode = m_ToolManager->GetReferenceData(0); bool hasReferenceNode = referenceNode != nullptr; mitk::DataNode* workingNode = m_ToolManager->GetWorkingData(0); bool hasWorkingNode = workingNode != nullptr; m_Controls->newSegmentationButton->setEnabled(false); if (hasReferenceNode) { m_Controls->newSegmentationButton->setEnabled(true); } if (hasWorkingNode && hasReferenceNode) { int layer = -1; referenceNode->GetIntProperty("layer", layer); workingNode->SetIntProperty("layer", layer + 1); } - this->UpdateInterpolatorWidget(); m_Controls->layersWidget->UpdateGUI(); m_Controls->labelsWidget->UpdateGUI(); this->ValidateSelectionInput(); } -void QmitkSegmentationView::UpdateInterpolatorWidget() -{ - m_Controls->slicesInterpolator->setEnabled(false); - - if (m_WorkingNode.IsNull()) - { - return; - } - - auto labelSetImage = dynamic_cast(m_WorkingNode->GetData()); - if (nullptr == labelSetImage) - { - return; - } - - int numberOfLabels = labelSetImage->GetNumberOfLabels(labelSetImage->GetActiveLayer()); - if (2 == numberOfLabels) // fix for T27319: exterior is label 0, first label is label 1 - { - m_Controls->interpolatorWarningLabel->hide(); - m_Controls->slicesInterpolator->setEnabled(true); - } - else - { - m_Controls->interpolatorWarningLabel->show(); - m_Controls->interpolatorWarningLabel->setText("Interpolation only works for single label segmentations."); - } -} - void QmitkSegmentationView::ValidateSelectionInput() { this->UpdateWarningLabel(""); m_Controls->layersWidget->setEnabled(false); m_Controls->labelsWidget->setEnabled(false); m_Controls->labelSetWidget->setEnabled(false); // the argument is actually not used // enable status depends on the tool manager selection m_Controls->toolSelectionBox2D->setEnabled(false); m_Controls->toolSelectionBox3D->setEnabled(false); + m_Controls->slicesInterpolator->setEnabled(false); + m_Controls->interpolatorWarningLabel->hide(); mitk::DataNode* referenceNode = m_Controls->referenceNodeSelector->GetSelectedNode(); mitk::DataNode* workingNode = m_Controls->workingNodeSelector->GetSelectedNode(); if (nullptr == referenceNode) { return; } if (nullptr == workingNode) { return; } mitk::IRenderWindowPart* renderWindowPart = this->GetRenderWindowPart(); auto workingNodeIsVisible = renderWindowPart && workingNode->IsVisible(renderWindowPart->GetQmitkRenderWindow("axial")->GetRenderer()); if (!workingNodeIsVisible) { this->UpdateWarningLabel(tr("The selected segmentation is currently not visible!")); return; } /* * Here we check whether the geometry of the selected segmentation image is aligned with the worldgeometry. * At the moment it is not supported to use a geometry different from the selected image for reslicing. * For further information see Bug 16063 */ const mitk::BaseGeometry* workingNodeGeo = workingNode->GetData()->GetGeometry(); const mitk::BaseGeometry* worldGeo = renderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); if (nullptr != workingNodeGeo && nullptr != worldGeo) { if (mitk::Equal(*workingNodeGeo->GetBoundingBox(), *worldGeo->GetBoundingBox(), mitk::eps, true)) { m_ToolManager->SetReferenceData(referenceNode); m_ToolManager->SetWorkingData(workingNode); m_Controls->layersWidget->setEnabled(true); m_Controls->labelsWidget->setEnabled(true); m_Controls->labelSetWidget->setEnabled(true); m_Controls->toolSelectionBox2D->setEnabled(true); m_Controls->toolSelectionBox3D->setEnabled(true); + + auto labelSetImage = dynamic_cast(workingNode->GetData()); + if (nullptr != labelSetImage) + { + int numberOfLabels = labelSetImage->GetNumberOfLabels(labelSetImage->GetActiveLayer()); + if (2 == numberOfLabels) // fix for T27319: exterior is label 0, first label is label 1 + { + m_Controls->slicesInterpolator->setEnabled(true); + } + else + { + m_Controls->interpolatorWarningLabel->show(); + m_Controls->interpolatorWarningLabel->setText("Interpolation only works for single label segmentations."); + } + } return; } } m_ToolManager->SetReferenceData(referenceNode); m_ToolManager->SetWorkingData(nullptr); this->UpdateWarningLabel(tr("Please perform a reinit on the segmentation image!")); } void QmitkSegmentationView::UpdateWarningLabel(QString text) { if (text.size() == 0) { m_Controls->selectionWarningLabel->hide(); } else { m_Controls->selectionWarningLabel->show(); m_Controls->selectionWarningLabel->setText("" + text + ""); } } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h index d202af58f5..d1e0a97ff1 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.h @@ -1,153 +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. ============================================================================*/ #ifndef QMITKSEGMENTATIONVIEW_H #define QMITKSEGMENTATIONVIEW_H #include "ui_QmitkSegmentationViewControls.h" #include #include #include /** * @brief The segmentation view provides a set of tool to use different segmentation algorithms. * It provides two selection widgets to load an image node and a segmentation node * on which to perform the segmentation. Creating new segmentation nodes is also possible. * The available segmentation tools are grouped into "2D"- and "3D"-tools. * * Most segmentation tools / algorithms need some kind of user interaction, where the * user is asked to draw something in the image display or set some seed points / start values. * The tools also often provide additional propeties so that a user can modify the * algorithm's behavior. * * This class additionally provides options to work with different layers (create new layers, * switch between layers). * Moreover, a multilabel widget displays all the existing labels of a multilabel segmentation * for the currently active layer. * The multilabel widget allows to control the labels by creatin new one, removing existing ones, * showing / hiding single labels, merging labels, (re-)naming them etc. * * Additionally the view provides an option to create "2D"- and "3D"-interpolations between * neighboring segmentation masks on unsegmented slices. * Interpolation for multilabel segmentations is currently not implemented. */ class QmitkSegmentationView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { Q_OBJECT public: static const std::string VIEW_ID; QmitkSegmentationView(); ~QmitkSegmentationView() override; private Q_SLOTS: // reaction to the selection of a new reference image in the selection widget void OnReferenceSelectionChanged(QList nodes); // reaction to the selection of a new segmentation image in the selection widget void OnSegmentationSelectionChanged(QList nodes); // reaction to the shortcut ("CTRL+H") for toggling the visibility of the working node void OnVisibilityShortcutActivated(); // reaction to the shortcut ("CTRL+L") for iterating over all labels void OnLabelToggleShortcutActivated(); // reaction to the button "New segmentation" void OnNewSegmentation(); void OnManualTool2DSelected(int id); void OnShowMarkerNodes(bool); void OnLayersChanged(); void OnLabelsChanged(); void OnShowLabelTable(bool); void OnGoToLabel(const mitk::Point3D &pos); void OnLabelSetWidgetReset(); private: void CreateQtPartControl(QWidget* parent) override; void SetFocus() override {} void RenderWindowPartActivated(mitk::IRenderWindowPart* renderWindowPart) override; void RenderWindowPartDeactivated(mitk::IRenderWindowPart* renderWindowPart) override; void OnPreferencesChanged(const berry::IBerryPreferences* prefs) override; void NodeAdded(const mitk::DataNode* node) override; void NodeRemoved(const mitk::DataNode* node) override; void OnEstablishLabelSetConnection(); void OnLooseLabelSetConnection(); // make sure all images / segmentations look according to the user preference settings void ApplyDisplayOptions(); // decorates a DataNode according to the user preference settings void ApplyDisplayOptions(mitk::DataNode* node); // If a contourmarker is selected, the plane in the related widget will be reoriented according to the marker`s geometry void OnContourMarkerSelected(const mitk::DataNode* node); void OnSelectionChanged(berry::IWorkbenchPart::Pointer part, const QList &nodes) override; void ResetMouseCursor(); void SetMouseCursor(const us::ModuleResource&, int hotspotX, int hotspotY); void UpdateGUI(); - void UpdateInterpolatorWidget(); - void ValidateSelectionInput(); void UpdateWarningLabel(QString text); + std::string GetDefaultLabelSetPreset() const; + QWidget* m_Parent; Ui::QmitkSegmentationViewControls* m_Controls; mitk::IRenderWindowPart* m_RenderWindowPart; mitk::ToolManager* m_ToolManager; mitk::DataNode::Pointer m_ReferenceNode; mitk::DataNode::Pointer m_WorkingNode; typedef std::map NodeTagMapType; NodeTagMapType m_WorkingDataObserverTags; unsigned int m_RenderingManagerObserverTag; mitk::NodePredicateAnd::Pointer m_ReferencePredicate; mitk::NodePredicateAnd::Pointer m_SegmentationPredicate; bool m_DrawOutline; bool m_SelectionMode; bool m_MouseCursorSet; + QString m_LabelSetPresetPreference; }; #endif // QMITKSEGMENTATIONVIEW_H diff --git a/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization.dox b/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization.dox index 267ea020c8..4414f06d79 100644 --- a/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization.dox +++ b/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization.dox @@ -1,140 +1,130 @@ /** \page org_mitk_views_volumevisualization The Volume Visualization Plugin \imageMacro{volume_visualization-dox.svg,"Icon of the Volume Visualization Plugin",2.00} \tableofcontents \section QVV_Overview Overview The Volume Visualization Plugin is a basic tool for volume rendering of three dimensional medical images. MITK provides generic transfer function presets for medical CT and MRT data. These functions that map the gray-value to color and opacity can be interactively edited. Additionally, there are controls to quickly generate commonly used transfer function shapes like the threshold and bell curve to help identify a range of grey-values. \imageMacro{QmitkVolumeVisualization_Overview.png,"",16.00} \section QVV_EnableVRPage Volume Rendering \subsection QVV_LoadingImage Select an image and enable volume rendering -\imageMacro{QmitkVolumeVisualization_Checkboxen.png,"",8.21} - -Select an image on top of the view and click on the checkbox left of 'Volumerendering'. Please be patient, while the image is prepared for rendering, which can take up to a half minute. +Select an image on top of the view and click on the checkbox left of 'Volume rendering'. \note Volume Visualization imposes following restrictions on images:
  • It has to be a 3D scalar image, that means e.g. a CT or MRT image.
  • 3D+t images are supported for rendering, but the histograms are not computed.
  • Also be aware that volume visualization requires a huge amount of memory. Very large images may not work unless you use the 64bit version.
-\subsection QVV_LODGPU Dropdown menus for the rendering and blend modes - -Two dropdown menus are located right next to the 'Volumerendering' checkbox. They allow you to select a rendering mode (Default, RayCast, GPU) and the blend mode (Composite, Max, Min, Avg, Add). +\subsection QVV_LODGPU Dropdown menu for the blend modes -Any rendering mode requires a lot of computing resources including processor, memory and often also graphics card. -The 'Default' selection usually finds the best 'rendering' mode for the available hardware. -Alternatively, it is possible to manually specify the selections 'RayCast' and 'GPU'. -The 'RayCast' selection is based on CPU computation and therefore typically slow, but allows to render without hardware acceleration. -The 'GPU' selection uses computing resources on the graphics card to accelerate volume rendering. -It requires a powerful graphics card and OpenGL hardware support for shaders but achieves much higher frame rates than software-rendering. +Two dropdown menu is located right next to the 'Volume rendering' checkbox. It allows you to select a blend mode (Composite, Maximim intensity, Minimum intensity, Average intensity, and Additive intensity). -Blend modes define how the volume voxels, intersected by the rendering rays, are pooled. The 'Composite' mode specifies standard volume rendering, -for which each voxel contributes equally with opacity and color. Other 'blend' modes simply visualize the voxel of the maximum / -minimum intensity and average / add the intensities along the rendering ray. +Blend modes define how the volume voxels, intersected by the rendering rays, are pooled. The 'Composite' mode specifies standard volume rendering, +for which each voxel contributes equally with opacity and color. Other 'blend' modes visualize the voxel of the maximum or minimum intensity or average or add the intensities along the rendering ray. \section QVV_PresetPage Applying premade presets \subsection QVV_Preset Internal presets There are some internal presets given that can be used with normal CT data (given in Houndsfield units). A large set of medical data has been tested with those presets, but they may not suit some special cases. Click on the 'Preset' tab for using internal or custom presets. \imageMacro{QmitkVolumeVisualization_InternalPresets.png,"",8.30}
  • 'CT Generic' is the default transfer function that is first applied.
  • 'CT Black&White' does not use any colors for the volume visualization as it may be distracting on some data.
  • 'CT Cardiac' is well-suited for CT images containing the heart.
  • 'CT Bone' emphasizes bones and shows other areas more transparent.
  • 'CT Bone (Gradient)' is like 'CT Bone' but shows only the surface from other organs by using the gradient.
  • 'MR Generic' is the default transfer function that can be used on MRT data (which is not normalized like CT data).
  • 'CT Thorax small' is useful if only a proportion of the thorax is selected to emphasize details.
  • 'CT Thorax large' is useful if a large proportion or the entire thorax is selected to emphasize details.
\subsection QVV_CustomPreset Saving and loading custom presets After creating or editing a transfer function (see \ref QVV_Editing or \ref QVV_ThresholdBell), the custom transfer function can be stored and later retrieved on the filesystem. Click 'Save' (respectively 'Load') button below the preset selection to save (load) the threshold-, color- and gradient function combined in a single .xml file. \section QVV_ThresholdBell Interactively create transfer functions Besides the possibility to directly edit the transfer functions (\ref QVV_Editing), the plugin provides two commonly known shapes to quickly generate transfer functions with a few clicks. Both generators have two parameters that can be modified by first clicking on the cross and then moving the mouse up/down and left/right. The first parameter 'center' (controlled by horizontal movement of the mouse) specifies the gray value where the center of the shape will be located. The second parameter 'width' (controlled by vertical movement of the mouse) specifies the width (or steepness) of the shape. \subsection Threshold Click on the 'Threshold' tab to activate the threshold function generator. \imageMacro{QmitkVolumeVisualization_Threshold.png,"",8.21} A threshold shape begins with zero and raises to one across the 'center' parameter. Lower widths result in steeper threshold functions. \subsection Bell Click on the 'Bell' tab to activate the bell-shaped threshold function generator. \imageMacro{QmitkVolumeVisualization_Bell.png,"",8.23} A threshold shape begins with zero and raises to one at the 'center' parameter and then lowers again to zero. The 'width' parameter corresponds to the width of the bell. \section QVV_Editing Customize transfer functions in detail \subsection QVV_Navigate Choosing gray value interval to edit \imageMacro{QmitkVolumeVisualization_Slider.png,"",8.23} To navigate across the gray value range or to zoom in some ranges use the 'range'-slider. All three function editors have in common following:
  • By left-clicking a new point is added.
  • By right-clicking a point is deleted.
  • By left-clicking and holding, an exisiting point can be dragged.
  • By pressing arrow keys, the currently selected point is moved.
  • By pressing the 'DELETE' key, the currently selected point is deleted.
  • Between points the transferfunctions are linear interpolated.
There are three transfer functions to customize: \subsection QVV_GO Grayvalue -> Opacity \imageMacro{QmitkVolumeVisualization_Opacity.png,"Gray values will be mapped to opacity.",8.04} An opacity of 0 means total transparent, an opacity of 1 means total opaque. The opacity editor allows changing the opacity for all gray values independently. You can alter the position of control points using your mouse. You can add control points by left-clicking on the graph. To remove a control point again you can right-click on the respective point. \subsection QVV_GC Grayvalue -> Color \imageMacro{QmitkVolumeVisualization_Color.png,"Gray values will be mapped to color.",8.81} The color transfer function editor also allows you to change its color by double-clicking a point. You can add color control points by left-clicking on the color bar. To remove a control point again right-click on the respective point. \subsection QVV_GGO Grayvalue and Gradient -> Opacity \imageMacro{QmitkVolumeVisualization_Gradient.png,"",8.85} The gradient editor allows you to change the gradient influence for all gray values independently. You can move the existing control points using your mouse. Additionally, you can add control points by left-clicking on the graph. To remove a control point again, right-click on the respective point. */ diff --git a/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization_Checkboxen.png b/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization_Checkboxen.png deleted file mode 100644 index dac50d7795..0000000000 Binary files a/Plugins/org.mitk.gui.qt.volumevisualization/documentation/UserManual/QmitkVolumeVisualization_Checkboxen.png and /dev/null differ diff --git a/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp b/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp index 90e0d7ecf0..b4e5ff02af 100755 --- a/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp +++ b/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.cpp @@ -1,313 +1,239 @@ /*============================================================================ 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 "QmitkVolumeVisualizationView.h" #include #include #include #include #include #include #include #include #include #include #include #include const std::string QmitkVolumeVisualizationView::VIEW_ID = "org.mitk.views.volumevisualization"; enum { DEFAULT_RENDERMODE = 0, RAYCAST_RENDERMODE = 1, GPU_RENDERMODE = 2 }; QmitkVolumeVisualizationView::QmitkVolumeVisualizationView() : QmitkAbstractView() , m_Controls(nullptr) { } void QmitkVolumeVisualizationView::SetFocus() { } void QmitkVolumeVisualizationView::CreateQtPartControl(QWidget* parent) { m_Controls = new Ui::QmitkVolumeVisualizationViewControls; m_Controls->setupUi(parent); m_Controls->volumeSelectionWidget->SetDataStorage(GetDataStorage()); m_Controls->volumeSelectionWidget->SetNodePredicate(mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), mitk::NodePredicateOr::New(mitk::NodePredicateDimension::New(3), mitk::NodePredicateDimension::New(4)), mitk::NodePredicateNot::New(mitk::NodePredicateProperty::New("helper object")))); m_Controls->volumeSelectionWidget->SetSelectionIsOptional(true); m_Controls->volumeSelectionWidget->SetEmptyInfo(QString("Please select a 3D / 4D image volume")); m_Controls->volumeSelectionWidget->SetPopUpTitel(QString("Select image volume")); // Fill the transfer function presets in the generator widget std::vector names; mitk::TransferFunctionInitializer::GetPresetNames(names); for (const auto& name : names) { m_Controls->transferFunctionGeneratorWidget->AddPreset(QString::fromStdString(name)); } - // see enum in vtkSmartVolumeMapper - m_Controls->renderMode->addItem("Default"); - m_Controls->renderMode->addItem("RayCast"); - m_Controls->renderMode->addItem("GPU"); - // see vtkVolumeMapper::BlendModes - m_Controls->blendMode->addItem("Comp"); - m_Controls->blendMode->addItem("Max"); - m_Controls->blendMode->addItem("Min"); - m_Controls->blendMode->addItem("Avg"); - m_Controls->blendMode->addItem("Add"); + m_Controls->blendMode->addItem("Composite", vtkVolumeMapper::COMPOSITE_BLEND); + m_Controls->blendMode->addItem("Maximum intensity", vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND); + m_Controls->blendMode->addItem("Minimum intensity", vtkVolumeMapper::MINIMUM_INTENSITY_BLEND); + m_Controls->blendMode->addItem("Average intensity", vtkVolumeMapper::AVERAGE_INTENSITY_BLEND); + m_Controls->blendMode->addItem("Additive", vtkVolumeMapper::ADDITIVE_BLEND); connect(m_Controls->volumeSelectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &QmitkVolumeVisualizationView::OnCurrentSelectionChanged); connect(m_Controls->enableRenderingCB, SIGNAL(toggled(bool)), this, SLOT(OnEnableRendering(bool))); - connect(m_Controls->renderMode, SIGNAL(activated(int)), this, SLOT(OnRenderMode(int))); connect(m_Controls->blendMode, SIGNAL(activated(int)), this, SLOT(OnBlendMode(int))); connect(m_Controls->transferFunctionGeneratorWidget, SIGNAL(SignalUpdateCanvas()), m_Controls->transferFunctionWidget, SLOT(OnUpdateCanvas())); connect(m_Controls->transferFunctionGeneratorWidget, SIGNAL(SignalTransferFunctionModeChanged(int)), SLOT(OnMitkInternalPreset(int))); m_Controls->enableRenderingCB->setEnabled(false); m_Controls->blendMode->setEnabled(false); - m_Controls->renderMode->setEnabled(false); m_Controls->transferFunctionWidget->setEnabled(false); m_Controls->transferFunctionGeneratorWidget->setEnabled(false); m_Controls->volumeSelectionWidget->SetAutoSelectNewNodes(true); this->m_TimePointChangeListener.RenderWindowPartActivated(this->GetRenderWindowPart()); connect(&m_TimePointChangeListener, &QmitkSliceNavigationListener::SelectedTimePointChanged, this, &QmitkVolumeVisualizationView::OnSelectedTimePointChanged); } void QmitkVolumeVisualizationView::RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_TimePointChangeListener.RenderWindowPartActivated(renderWindowPart); } void QmitkVolumeVisualizationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart) { this->m_TimePointChangeListener.RenderWindowPartDeactivated(renderWindowPart); } void QmitkVolumeVisualizationView::OnMitkInternalPreset(int mode) { if (m_SelectedNode.IsExpired()) { return; } auto node = m_SelectedNode.Lock(); mitk::TransferFunctionProperty::Pointer transferFuncProp; if (node->GetProperty(transferFuncProp, "TransferFunction")) { // first item is only information if (--mode == -1) return; // -- Creat new TransferFunction mitk::TransferFunctionInitializer::Pointer tfInit = mitk::TransferFunctionInitializer::New(transferFuncProp->GetValue()); tfInit->SetTransferFunctionMode(mode); RequestRenderWindowUpdate(); m_Controls->transferFunctionWidget->OnUpdateCanvas(); } } void QmitkVolumeVisualizationView::OnCurrentSelectionChanged(QList nodes) { m_SelectedNode = nullptr; if (nodes.empty() || nodes.front().IsNull()) { UpdateInterface(); return; } auto selectedNode = nodes.front(); auto image = dynamic_cast(selectedNode->GetData()); if (nullptr != image) { m_SelectedNode = selectedNode; } UpdateInterface(); } void QmitkVolumeVisualizationView::OnEnableRendering(bool state) { if (m_SelectedNode.IsExpired()) { return; } m_SelectedNode.Lock()->SetProperty("volumerendering", mitk::BoolProperty::New(state)); UpdateInterface(); RequestRenderWindowUpdate(); } -void QmitkVolumeVisualizationView::OnRenderMode(int mode) +void QmitkVolumeVisualizationView::OnBlendMode(int index) { if (m_SelectedNode.IsExpired()) - { return; - } auto selectedNode = m_SelectedNode.Lock(); - - bool usegpu = false; - bool useray = false; - if (DEFAULT_RENDERMODE == mode) - { - useray = true; - usegpu = true; - } - else if (GPU_RENDERMODE == mode) - { - usegpu = true; - } - else if (RAYCAST_RENDERMODE == mode) - { - useray = true; - } - - selectedNode->SetProperty("volumerendering.usegpu", mitk::BoolProperty::New(usegpu)); - selectedNode->SetProperty("volumerendering.useray", mitk::BoolProperty::New(useray)); - - RequestRenderWindowUpdate(); -} - -void QmitkVolumeVisualizationView::OnBlendMode(int mode) -{ - if (m_SelectedNode.IsExpired()) - { - return; - } - - auto selectedNode = m_SelectedNode.Lock(); - - bool usemip = false; - if (vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND == mode) - { - usemip = true; - } - - selectedNode->SetProperty("volumerendering.usemip", mitk::BoolProperty::New(usemip)); + int mode = m_Controls->blendMode->itemData(index).toInt(); selectedNode->SetProperty("volumerendering.blendmode", mitk::IntProperty::New(mode)); - RequestRenderWindowUpdate(); + this->RequestRenderWindowUpdate(); } void QmitkVolumeVisualizationView::OnSelectedTimePointChanged(const mitk::TimePointType & /*newTimePoint*/) { this->UpdateInterface(); } void QmitkVolumeVisualizationView::UpdateInterface() { if (m_SelectedNode.IsExpired()) { // turnoff all m_Controls->enableRenderingCB->setChecked(false); m_Controls->enableRenderingCB->setEnabled(false); m_Controls->blendMode->setCurrentIndex(0); m_Controls->blendMode->setEnabled(false); - m_Controls->renderMode->setCurrentIndex(0); - m_Controls->renderMode->setEnabled(false); - m_Controls->transferFunctionWidget->SetDataNode(nullptr); m_Controls->transferFunctionWidget->setEnabled(false); m_Controls->transferFunctionGeneratorWidget->SetDataNode(nullptr); m_Controls->transferFunctionGeneratorWidget->setEnabled(false); return; } bool enabled = false; auto selectedNode = m_SelectedNode.Lock(); selectedNode->GetBoolProperty("volumerendering", enabled); m_Controls->enableRenderingCB->setEnabled(true); m_Controls->enableRenderingCB->setChecked(enabled); if (!enabled) { // turnoff all except volumerendering checkbox m_Controls->blendMode->setCurrentIndex(0); m_Controls->blendMode->setEnabled(false); - m_Controls->renderMode->setCurrentIndex(0); - m_Controls->renderMode->setEnabled(false); - m_Controls->transferFunctionWidget->SetDataNode(nullptr); m_Controls->transferFunctionWidget->setEnabled(false); m_Controls->transferFunctionGeneratorWidget->SetDataNode(nullptr); m_Controls->transferFunctionGeneratorWidget->setEnabled(false); return; } // otherwise we can activate em all m_Controls->blendMode->setEnabled(true); - m_Controls->renderMode->setEnabled(true); // Determine Combo Box mode - { - bool usegpu = false; - bool useray = false; - bool usemip = false; - selectedNode->GetBoolProperty("volumerendering.usegpu", usegpu); - selectedNode->GetBoolProperty("volumerendering.useray", useray); - selectedNode->GetBoolProperty("volumerendering.usemip", usemip); - - int blendMode; - if (selectedNode->GetIntProperty("volumerendering.blendmode", blendMode)) - m_Controls->blendMode->setCurrentIndex(blendMode); + int blendMode; + if (selectedNode->GetIntProperty("volumerendering.blendmode", blendMode)) + m_Controls->blendMode->setCurrentIndex(blendMode); - if (usemip) - m_Controls->blendMode->setCurrentIndex(vtkVolumeMapper::MAXIMUM_INTENSITY_BLEND); - - int mode = DEFAULT_RENDERMODE; - - if (useray) - mode = RAYCAST_RENDERMODE; - else if (usegpu) - mode = GPU_RENDERMODE; - - m_Controls->renderMode->setCurrentIndex(mode); - } auto time = this->GetRenderWindowPart()->GetTimeNavigationController()->GetSelectedTimeStep(); m_Controls->transferFunctionWidget->SetDataNode(selectedNode, time); m_Controls->transferFunctionWidget->setEnabled(true); m_Controls->transferFunctionGeneratorWidget->SetDataNode(selectedNode, time); m_Controls->transferFunctionGeneratorWidget->setEnabled(true); } diff --git a/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.h b/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.h index 658d5fc133..51e5949bec 100755 --- a/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.h +++ b/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationView.h @@ -1,69 +1,68 @@ /*============================================================================ 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 QMITKVOLUMEVISUALIZATIONVIEW_H #define QMITKVOLUMEVISUALIZATIONVIEW_H #include "ui_QmitkVolumeVisualizationViewControls.h" // mitk core #include #include #include #include #include /** * @brief */ class QmitkVolumeVisualizationView : public QmitkAbstractView, public mitk::IRenderWindowPartListener { Q_OBJECT public: static const std::string VIEW_ID; QmitkVolumeVisualizationView(); ~QmitkVolumeVisualizationView() override = default; void SetFocus() override; private Q_SLOTS: void OnCurrentSelectionChanged(QList nodes); void OnMitkInternalPreset(int mode); void OnEnableRendering(bool state); - void OnRenderMode(int mode); - void OnBlendMode(int mode); + void OnBlendMode(int index); void OnSelectedTimePointChanged(const mitk::TimePointType &); protected: void RenderWindowPartActivated(mitk::IRenderWindowPart *renderWindowPart) override; void RenderWindowPartDeactivated(mitk::IRenderWindowPart *renderWindowPart) override; private: void CreateQtPartControl(QWidget* parent) override; void UpdateInterface(); Ui::QmitkVolumeVisualizationViewControls* m_Controls; mitk::WeakPointer m_SelectedNode; QmitkSliceNavigationListener m_TimePointChangeListener; }; #endif // QMITKVOLUMEVISUALIZATIONVIEW_H diff --git a/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationViewControls.ui b/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationViewControls.ui index 94fd15064f..1ed8af665d 100644 --- a/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationViewControls.ui +++ b/Plugins/org.mitk.gui.qt.volumevisualization/src/internal/QmitkVolumeVisualizationViewControls.ui @@ -1,128 +1,132 @@ QmitkVolumeVisualizationViewControls 0 0 - 300 + 319 600 0 0 Volume visualization - + Selected image: 0 0 0 40 - + + + + 0 + 0 + + - Click this checkbox to enable volumerendering in the 3D view of the selected image + Enable volume rendering of the selected image in the 3D view - Volumerendering + Volume rendering - - - - 0 - 1 - - - - Select render mode + + + Blend mode: - + + + Switch between different blend modes + + 0 0 0 0 0 1 QmitkSingleNodeSelectionWidget QWidget
QmitkSingleNodeSelectionWidget.h
QmitkTransferFunctionWidget QWidget
QmitkTransferFunctionWidget.h
QmitkTransferFunctionGeneratorWidget QWidget
QmitkTransferFunctionGeneratorWidget.h
1