diff --git a/CMake/PackageDepends/MITK_httplib_Config.cmake b/CMake/PackageDepends/MITK_httplib_Config.cmake new file mode 100644 index 0000000000..a20aa2233b --- /dev/null +++ b/CMake/PackageDepends/MITK_httplib_Config.cmake @@ -0,0 +1 @@ +set(ALL_LIBRARIES httplib::httplib) diff --git a/CMakeExternals/ExternalProjectList.cmake b/CMakeExternals/ExternalProjectList.cmake index 0ce05a09a6..dfd6ab5826 100644 --- a/CMakeExternals/ExternalProjectList.cmake +++ b/CMakeExternals/ExternalProjectList.cmake @@ -1,29 +1,30 @@ mitkFunctionAddExternalProject(NAME Poco ON COMPONENTS Foundation Net Util XML Zip) mitkFunctionAddExternalProject(NAME DCMTK ON DOC "EXPERIMENTAL, superbuild only: Use DCMTK in MITK") mitkFunctionAddExternalProject(NAME tinyxml2 ON ADVANCED) mitkFunctionAddExternalProject(NAME GDCM ON ADVANCED) mitkFunctionAddExternalProject(NAME Boost ON NO_CACHE) mitkFunctionAddExternalProject(NAME ANN ON ADVANCED DOC "Use Approximate Nearest Neighbor Library") mitkFunctionAddExternalProject(NAME CppUnit ON ADVANCED DOC "Use CppUnit for unit tests") mitkFunctionAddExternalProject(NAME HDF5 ON ADVANCED) mitkFunctionAddExternalProject(NAME ITK ON NO_CACHE DEPENDS HDF5) mitkFunctionAddExternalProject(NAME VTK ON NO_CACHE) mitkFunctionAddExternalProject(NAME ZLIB OFF ADVANCED) mitkFunctionAddExternalProject(NAME lz4 ON ADVANCED) mitkFunctionAddExternalProject(NAME cpprestsdk OFF DEPENDS Boost ZLIB ADVANCED) mitkFunctionAddExternalProject(NAME ACVD OFF DOC "Use Approximated Centroidal Voronoi Diagrams") mitkFunctionAddExternalProject(NAME CTK ON DEPENDS Qt6 DCMTK DOC "Use CTK in MITK") mitkFunctionAddExternalProject(NAME DCMQI ON DEPENDS DCMTK ITK DOC "Use dcmqi in MITK") mitkFunctionAddExternalProject(NAME MatchPoint OFF ADVANCED DEPENDS Boost ITK DOC "Use the MatchPoint translation image registration library") mitkFunctionAddExternalProject(NAME nlohmann_json ON ADVANCED) +mitkFunctionAddExternalProject(NAME httplib ON DEPENDS ZLIB) if(MITK_USE_Qt6) mitkFunctionAddExternalProject(NAME Qt6Qwt6 ON ADVANCED DEPENDS Qt6) endif() if(UNIX AND NOT APPLE) mitkFunctionAddExternalProject(NAME PCRE OFF ADVANCED NO_PACKAGE) mitkFunctionAddExternalProject(NAME SWIG OFF ADVANCED NO_PACKAGE DEPENDS PCRE) elseif(WIN32) mitkFunctionAddExternalProject(NAME SWIG OFF ADVANCED NO_PACKAGE) endif() diff --git a/CMakeExternals/httplib.cmake b/CMakeExternals/httplib.cmake new file mode 100644 index 0000000000..0537909e10 --- /dev/null +++ b/CMakeExternals/httplib.cmake @@ -0,0 +1,44 @@ +set(proj httplib) +set(proj_DEPENDENCIES ZLIB) + +if(MITK_USE_${proj}) + set(${proj}_DEPENDS ${proj}) + + if(DEFINED ${proj}_DIR AND NOT EXISTS ${${proj}_DIR}) + message(FATAL_ERROR "${proj}_DIR variable is defined but corresponds to non-existing directory!") + endif() + + if(NOT DEFINED ${proj}_DIR) + set(cmake_cache_args + ${ep_common_cache_args} + -DHTTPLIB_REQUIRE_OPENSSL:BOOL=ON + -DHTTPLIB_REQUIRE_ZLIB:BOOL=ON + -DHTTPLIB_USE_BROTLI_IF_AVAILABLE:BOOL=OFF + ) + + if(OPENSSL_ROOT_DIR) + list(APPEND cmake_cache_args + -DOPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR} + ) + endif() + + if(OPENSSL_VERSION VERSION_GREATER_EQUAL 3) + set(GIT_TAG 5c00bbf36ba8ff47b4fb97712fc38cb2884e5b98) # v0.15.3 cpp-httplib + else() + set(GIT_TAG cbca63f091ef1147ff57e90eb1ee5e558aa05d2c) # v0.14.3 cpp-httplib fallback version with OPENSSLv1 support + endif() + + ExternalProject_Add(${proj} + GIT_REPOSITORY https://github.com/yhirose/cpp-httplib.git + GIT_TAG ${GIT_TAG} + CMAKE_ARGS ${ep_common_args} + CMAKE_CACHE_ARGS ${cmake_cache_args} + CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} + DEPENDS ${proj_DEPENDENCIES} + ) + + set(${proj}_DIR ${ep_prefix}) + else() + mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") + endif() +endif() diff --git a/CMakeLists.txt b/CMakeLists.txt index db6bc24f3d..760939ba18 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1413 +1,1413 @@ #[[ When increasing the minimum required version, check if Boost_ADDITIONAL_VERSIONS in CMake/PackageDepends/MITK_Boost_Config.cmake can be removed. See the first long comment in CMakeExternals/Boost.cmake for details. ]] set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.18) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.19 AND CMAKE_VERSION VERSION_LESS 3.19.2) message(FATAL_ERROR "\ CMake v${CMAKE_VERSION} is defective [1]. \ Please either downgrade to v3.18 or upgrade to at least v3.19.2.\n\ [1] https://gitlab.kitware.com/cmake/cmake/-/issues/21529") endif() #----------------------------------------------------------------------------- # Policies #----------------------------------------------------------------------------- #[[ T28060 https://cmake.org/cmake/help/v3.18/policy/CMP0091.html https://cmake.org/cmake/help/v3.18/variable/CMAKE_MSVC_RUNTIME_LIBRARY.html We pass CMP0091 to all external projects as command-line argument: -DCMAKE_POLICY_DEFAULT_CMP0091:STRING=OLD ]] cmake_policy(SET CMP0091 OLD) if(POLICY CMP0135) cmake_policy(SET CMP0135 NEW) # https://cmake.org/cmake/help/v3.24/policy/CMP0135.html endif() #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK VERSION 2023.12.99) include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # MITK Extension Feature #----------------------------------------------------------------------------- set(MITK_EXTENSION_DIRS "" CACHE STRING "") unset(MITK_ABSOLUTE_EXTENSION_DIRS) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) get_filename_component(MITK_ABSOLUTE_EXTENSION_DIR "${MITK_EXTENSION_DIR}" ABSOLUTE) list(APPEND MITK_ABSOLUTE_EXTENSION_DIRS "${MITK_ABSOLUTE_EXTENSION_DIR}") endforeach() set(MITK_DIR_PLUS_EXTENSION_DIRS "${MITK_SOURCE_DIR}" ${MITK_ABSOLUTE_EXTENSION_DIRS}) #----------------------------------------------------------------------------- # Update CMake module path #----------------------------------------------------------------------------- set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake) set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake") if(EXISTS "${MITK_CMAKE_EXTENSION_DIR}") list(APPEND CMAKE_MODULE_PATH "${MITK_CMAKE_EXTENSION_DIR}") endif() endforeach() #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- # Standard CMake macros include(FeatureSummary) include(CTest) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionEnableBuildConfiguration) include(mitkFunctionWhitelists) include(mitkFunctionAddExternalProject) include(mitkFunctionAddLibrarySearchPaths) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) else() set(GCC_VERSION 0) endif() set(MITK_CXX_STANDARD 17) set(CMAKE_CXX_EXTENSIONS 0) set(CMAKE_CXX_STANDARD ${MITK_CXX_STANDARD}) set(CMAKE_CXX_STANDARD_REQUIRED 1) # This is necessary to avoid problems with compile feature checks. # CMAKE_CXX_STANDARD seems to only set the -std=c++ flag for targets. # However, compile flag checks also need to be done with -std=c++. # The MITK_CXX_FLAG variable is also used for external projects # build during the MITK super-build. mitkFunctionCheckCompilerFlags("-std=c++${MITK_CXX_STANDARD}" MITK_CXX${MITK_CXX_STANDARD}_FLAG) #----------------------------------------------------------------------------- # Warn if source or build path is too long #----------------------------------------------------------------------------- if(WIN32) set(_src_dir_length_max 50) set(_bin_dir_length_max 50) if(MITK_USE_SUPERBUILD) set(_src_dir_length_max 34) # _src_dir_length_max - strlen(ep/src/ITK-build) set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build) endif() string(LENGTH "${MITK_SOURCE_DIR}" _src_n) string(LENGTH "${MITK_BINARY_DIR}" _bin_n) # The warnings should be converted to errors if(_src_n GREATER _src_dir_length_max) message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- # ----------------------------------------- # General build options option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) option(MITK_FAST_TESTING "Disable long-running tests like packaging" OFF) option(MITK_XVFB_TESTING "Execute test drivers through xvfb-run" OFF) option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) option(MITK_BUILD_EXAMPLES "Build the MITK Examples" OFF) mark_as_advanced( MITK_XVFB_TESTING MITK_FAST_TESTING MITK_BUILD_ALL_APPS ) #----------------------------------------------------------------------------- # Set UI testing flags #----------------------------------------------------------------------------- if(MITK_XVFB_TESTING) set(MITK_XVFB_TESTING_COMMAND "xvfb-run" "--auto-servernum" CACHE STRING "Command and options to test through Xvfb") mark_as_advanced(MITK_XVFB_TESTING_COMMAND) endif(MITK_XVFB_TESTING) # ----------------------------------------- # Other options set(MITK_CUSTOM_REVISION_DESC "" CACHE STRING "Override MITK revision description") mark_as_advanced(MITK_CUSTOM_REVISION_DESC) set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "") include(CMakeExternals/ExternalProjectList.cmake) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMakeExternals") if(EXISTS "${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") include("${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake") endif() endforeach() # ----------------------------------------- # Other MITK_USE_* options not related to # external projects build via the # MITK superbuild option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_OpenMP "Use OpenMP" OFF) option(MITK_USE_Python3 "Use Python 3" OFF) #----------------------------------------------------------------------------- # Build configurations #----------------------------------------------------------------------------- set(_buildConfigs "Custom") file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake) foreach(_buildConfigFile ${_buildConfigFiles}) get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_buildConfigFile}) endforeach() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) file(GLOB _extBuildConfigFiles "${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake") foreach(_extBuildConfigFile ${_extBuildConfigFiles}) get_filename_component(_extBuildConfigFile "${_extBuildConfigFile}" NAME_WE) list(APPEND _buildConfigs "${_extBuildConfigFile}") endforeach() list(REMOVE_DUPLICATES _buildConfigs) endforeach() set(MITK_BUILD_CONFIGURATION "WorkbenchRelease" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs}) mitkFunctionEnableBuildConfiguration() mitkFunctionCreateWhitelistPaths(MITK) mitkFunctionFindWhitelists(MITK) # ----------------------------------------- # Qt version related variables option(MITK_USE_Qt6 "Use Qt 6 library" ON) if(MITK_USE_Qt6) set(MITK_QT6_MINIMUM_VERSION 6.6) set(MITK_QT6_COMPONENTS Concurrent Core Core5Compat CoreTools Designer DesignerComponentsPrivate Gui Help LinguistTools Network OpenGL OpenGLWidgets Qml Sql StateMachine Svg ToolsTools UiTools WebEngineCore WebEngineWidgets Widgets Xml ) if(APPLE) list(APPEND MITK_QT6_COMPONENTS DBus) endif() # Hint at default install locations of Qt if(NOT Qt6_DIR) if(MSVC) set(_dir_candidates "C:/Qt") if(CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") elseif(CMAKE_GENERATOR MATCHES "Ninja") include(mitkFunctionGetMSVCVersion) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") endif() endif() if(_compilers MATCHES "[0-9]+") if (CMAKE_MATCH_0 EQUAL 2022) list(APPEND _compilers "msvc2019") # Binary compatible endif() endif() else() set(_dir_candidates ~/Qt) if(APPLE) set(_compilers clang) else() list(APPEND _dir_candidates /opt/Qt) set(_compilers gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) foreach(_compiler ${_compilers}) list(APPEND _compilers64 "${_compiler}_64") endforeach() set(_compilers ${_compilers64}) endif() if(APPLE) list(APPEND _compilers macos) endif() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) foreach(_compiler ${_compilers}) set(_glob_expression "${_dir_candidate}/6.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT6_HINTS ${_hints}) endforeach() endforeach() endif() find_package(Qt6 ${MITK_QT6_MINIMUM_VERSION} COMPONENTS ${MITK_QT6_COMPONENTS} REQUIRED HINTS ${MITK_QT6_HINTS}) get_target_property(QT_QMAKE_EXECUTABLE Qt6::qmake LOCATION) get_target_property(QT_HELPGENERATOR_EXECUTABLE Qt6::qhelpgenerator LOCATION) endif() if(Qt6_DIR) list(APPEND CMAKE_PREFIX_PATH "${Qt6_DIR}/../../..") list(REMOVE_DUPLICATES CMAKE_PREFIX_PATH) endif() # ----------------------------------------- # Custom dependency logic if(WIN32 AND Qt6_DIR) set(_dir_candidate "${Qt6_DIR}/../../../../../Tools/OpenSSLv3/Win_x64") get_filename_component(_dir_candidate ${_dir_candidate} ABSOLUTE) if(EXISTS "${_dir_candidate}") set(OPENSSL_ROOT_DIR "${_dir_candidate}") endif() endif() find_package(OpenSSL 3) if(NOT OpenSSL_FOUND) find_package(OpenSSL 1.1.1) endif() option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") -if(MITK_USE_cpprestsdk) +if(MITK_USE_cpprestsdk OR MITK_USE_httplib) if(NOT OpenSSL_FOUND) - set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n") + set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK and cpp-httplib).\n") if(UNIX) if(APPLE) set(openssl_message "${openssl_message}Please install it using your favorite package management " "system (i.e. Homebrew or MacPorts).\n") else() set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n") endif() else() set(openssl_message "${openssl_message}Please either install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\n" - "Or use the Qt Maintenance tool to install:\n" + "Or use the Qt Maintenance tool to install (recommended):\n" " Developer and Designer Tools > OpenSSL Toolkit > OpenSSL 64-bit binaries\n") endif() set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by " "adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an " "OpenSSL installation. Make sure to clear variables of partly found " "versions of OpenSSL before, or they will be mixed up.") message(FATAL_ERROR ${openssl_message}) endif() list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system) if(UNIX) list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread) endif() list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES) set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE) endif() if(MITK_USE_Python3) set(MITK_USE_ZLIB ON CACHE BOOL "" FORCE) if(APPLE) set(python3_mininum_version 3.11) else() set(python3_mininum_version 3.8) endif() find_package(Python3 ${python3_mininum_version} REQUIRED COMPONENTS Interpreter Development NumPy) if(WIN32) string(REPLACE "\\" "/" Python3_STDARCH "${Python3_STDARCH}") string(REPLACE "\\" "/" Python3_STDLIB "${Python3_STDLIB}") string(REPLACE "\\" "/" Python3_SITELIB "${Python3_SITELIB}") endif() endif() if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() #----------------------------------------------------------------------------- # Pixel type multiplexing #----------------------------------------------------------------------------- # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}) string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}) foreach(_scalar_type ${_integral_types} ${_floating_types}) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,") endforeach() string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length) math(EXPR _length "${_length} - 1") string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() find_package(Git REQUIRED) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") # Print configuration summary message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL) return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # Organize MITK targets in folders #----------------------------------------------------------------------------- set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(MITK_ROOT_FOLDER "MITK" CACHE STRING "") mark_as_advanced(MITK_ROOT_FOLDER) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(GenerateExportHeader) include(mitkFunctionAddManifest) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionUseModules) if( ${MITK_USE_MatchPoint} ) include(mitkFunctionCreateMatchPointDeployedAlgorithm) endif() include(mitkMacroConfigureItkPixelTypes) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) #----------------------------------------------------------------------------- # Global CMake variables #----------------------------------------------------------------------------- if(NOT DEFINED CMAKE_DEBUG_POSTFIX) # We can't do this yet because the CTK Plugin Framework # cannot cope with a postfix yet. #set(CMAKE_DEBUG_POSTFIX d) endif() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- set(_default_LIBRARY_output_dir lib) set(_default_RUNTIME_output_dir bin) set(_default_ARCHIVE_output_dir lib) foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) else() set(CMAKE_${type}_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${_default_${type}_output_dir}) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY}) endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY} CACHE INTERNAL "Output directory for ${type} files.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- if(OpenSSL_FOUND AND WIN32) #[[ On Windows, CMake is able to locate the link libraries for OpenSSL but it does not look for the corresponding DLLs that we need to copy to our binary directories and include in packaging. Setting these paths manually is cumbersome so we try to use a simple heuristic to automatically set them: - Based on the link libraries (usually located in a lib folder), try to find the "../bin" binary directory. - Use the base file names of the link libraries to find corresponding DLLs like "*.dll", that usually are named like "-1_1-x64.dll" or similar. ]] set(openssl_ssl_dll "") set(openssl_crypto_dll "") if(OPENSSL_SSL_LIBRARY AND EXISTS "${OPENSSL_SSL_LIBRARY}") get_filename_component(openssl_bin_dir "${OPENSSL_SSL_LIBRARY}" DIRECTORY) get_filename_component(openssl_bin_dir "${openssl_bin_dir}" DIRECTORY) set(openssl_bin_dir "${openssl_bin_dir}/bin") if(EXISTS "${openssl_bin_dir}") get_filename_component(openssl_ssl_basename "${OPENSSL_SSL_LIBRARY}" NAME_WE) file(GLOB openssl_ssl_dll "${openssl_bin_dir}/${openssl_ssl_basename}*.dll") list(LENGTH openssl_ssl_dll num_findings) if(num_findings GREATER 1) set(openssl_ssl_dll "") endif() get_filename_component(openssl_crypto_basename "${OPENSSL_CRYPTO_LIBRARY}" NAME_WE) file(GLOB openssl_crypto_dll "${openssl_bin_dir}/${openssl_crypto_basename}*.dll") list(LENGTH openssl_crypto_dll num_findings) if(num_findings GREATER 1) set(openssl_crypto_dll "") endif() endif() endif() set(MITK_OPENSSL_SSL_DLL "${openssl_ssl_dll}" CACHE FILEPATH "") if(DEFINED CACHE{MITK_OPENSSL_SSL_DLL} AND NOT MITK_OPENSSL_SSL_DLL AND openssl_ssl_dll) set(MITK_OPENSSL_SSL_DLL "${openssl_ssl_dll}" CACHE FILEPATH "" FORCE) endif() set(MITK_OPENSSL_CRYPTO_DLL "${openssl_crypto_dll}" CACHE FILEPATH "") if(DEFINED CACHE{MITK_OPENSSL_CRYPTO_DLL} AND NOT MITK_OPENSSL_CRYPTO_DLL AND openssl_crypto_dll) set(MITK_OPENSSL_CRYPTO_DLL "${openssl_crypto_dll}" CACHE FILEPATH "" FORCE) endif() if(MITK_OPENSSL_SSL_DLL AND EXISTS "${MITK_OPENSSL_SSL_DLL}" AND MITK_OPENSSL_CRYPTO_DLL AND EXISTS "${MITK_OPENSSL_CRYPTO_DLL}") foreach(config_type ${CMAKE_CONFIGURATION_TYPES}) execute_process(COMMAND "${CMAKE_COMMAND}" -E make_directory "${MITK_BINARY_DIR}/bin/${config_type}") configure_file("${MITK_OPENSSL_SSL_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) configure_file("${MITK_OPENSSL_CRYPTO_DLL}" "${MITK_BINARY_DIR}/bin/${config_type}/" COPYONLY) endforeach() MITK_INSTALL(FILES "${MITK_OPENSSL_SSL_DLL}" "${MITK_OPENSSL_CRYPTO_DLL}" ) endif() endif() # Look for optional Doxygen package find_package(Doxygen) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) # Ask the user to show the console window for applications option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) if(NOT MITK_FAST_TESTING) if(MITK_CTEST_SCRIPT_MODE STREQUAL "Continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "Experimental") set(MITK_FAST_TESTING ON) endif() endif() if(NOT UNIX) set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms") endif() if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() # Configure pixel types used for ITK image access multiplexing mitkMacroConfigureItkPixelTypes() # Configure module naming conventions set(MITK_MODULE_NAME_REGEX_MATCH "^[A-Z].*$") set(MITK_MODULE_NAME_REGEX_NOT_MATCH "^[Mm][Ii][Tt][Kk].*$") set(MITK_DEFAULT_MODULE_NAME_PREFIX "Mitk") set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) set(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME 1) #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) # MITK_VERSION set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") if(MITK_VERSION_PATCH STREQUAL "99") set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}") endif() #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on macOS all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() endforeach() endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_CXX${MITK_CXX_STANDARD}_FLAG}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) if(WIN32) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DWIN32_LEAN_AND_MEAN -DNOMINMAX") mitkFunctionCheckCompilerFlags("/wd4005" MITK_CXX_FLAGS) # warning C4005: macro redefinition mitkFunctionCheckCompilerFlags("/wd4231" MITK_CXX_FLAGS) # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation # the following line should be removed after fixing bug 17637 mitkFunctionCheckCompilerFlags("/wd4316" MITK_CXX_FLAGS) # warning C4316: object alignment on heap mitkFunctionCheckCompilerFlags("/wd4180" MITK_CXX_FLAGS) # warning C4180: qualifier applied to function type has no meaning mitkFunctionCheckCompilerFlags("/wd4251" MITK_CXX_FLAGS) # warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' endif() if(APPLE) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DGL_SILENCE_DEPRECATION") # Apple deprecated OpenGL in macOS 10.14 endif() if(NOT MSVC_VERSION) foreach(_flag -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Wno-error=gnu -Wno-error=unknown-pragmas # The strict-overflow warning is generated by ITK template code -Wno-error=strict-overflow -Woverloaded-virtual -Wstrict-null-sentinel #-Wold-style-cast #-Wsign-promo -Wno-deprecated-copy -Wno-array-bounds -Wno-cast-function-type -Wno-maybe-uninitialized -Wno-error=stringop-overread -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) set(MITK_CXX_FLAGS_RELEASE "-U_FORTIFY_SOURCES -D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/CMake/PackageDepends") if(EXISTS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") list(APPEND MODULES_PACKAGE_DEPENDS_DIRS "${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}") endif() endforeach() if(NOT MITK_USE_SYSTEM_Boost) set(Boost_NO_SYSTEM_PATHS 1) endif() set(Boost_USE_MULTITHREADED 1) set(Boost_USE_STATIC_LIBS 0) set(Boost_USE_STATIC_RUNTIME 0) set(Boost_ADDITIONAL_VERSIONS 1.74 1.74.0) # We need this later for a DCMTK workaround set(_dcmtk_dir_orig ${DCMTK_DIR}) # This property is populated at the top half of this file get_property(MITK_EXTERNAL_PROJECTS GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(MITK_USE_${ep} AND _package) if(_components) find_package(${_package} COMPONENTS ${_components} REQUIRED CONFIG) else() # Prefer config mode first because it finds external # Config.cmake files pointed at by _DIR variables. # Otherwise, existing Find.cmake files could fail. if(DEFINED ${_package}_DIR) #we store the information because it will be overwritten by find_package #and would get lost for all EPs that use on Find.cmake instead of config #files. set(_temp_EP_${_package}_dir ${${_package}_DIR}) endif(DEFINED ${_package}_DIR) find_package(${_package} QUIET CONFIG) string(TOUPPER "${_package}" _package_uc) if(NOT (${_package}_FOUND OR ${_package_uc}_FOUND)) if(DEFINED _temp_EP_${_package}_dir) set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externally set dir of the package ${_package}" FORCE) endif(DEFINED _temp_EP_${_package}_dir) find_package(${_package} REQUIRED) endif() endif() endif() endforeach() # Ensure that the MITK CMake module path comes first set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) if(MITK_USE_DCMTK) if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*") # Help our FindDCMTK.cmake script find our super-build DCMTK set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) else() # Use the original value set(DCMTK_DIR ${_dcmtk_dir_orig}) endif() endif() if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful # package information. We explicitly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() if(MITK_USE_OpenMP) find_package(OpenMP REQUIRED COMPONENTS CXX) else() find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) elseif(APPLE AND OpenMP_libomp_LIBRARY AND NOT OpenMP_CXX_LIB_NAMES) set(OpenMP_CXX_LIB_NAMES libomp CACHE STRING "" FORCE) get_filename_component(openmp_lib_dir "${OpenMP_libomp_LIBRARY}" DIRECTORY) set(openmp_include_dir "${openmp_lib_dir}/../include") if(EXISTS "${openmp_include_dir}") get_filename_component(openmp_include_dir "${openmp_include_dir}" REALPATH) set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${openmp_include_dir}" CACHE STRING "" FORCE) find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) endif() endif() endif() endif() # Qt support if(MITK_USE_Qt6) if(MITK_USE_BLUEBERRY) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP) # Sanity checks for in-application BlueBerry plug-in help generation if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT MITK_USE_Qt6) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt6 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE) endif() endif() if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP) message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON") endif() endif() endif() #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch (const std::exception& e) { fprintf(stderr, \"%s\\n\", e.what()); return EXIT_FAILURE; } catch (...) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; }") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Utilities) add_subdirectory(Modules) include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake") mitkFunctionWhitelistModules(MITK MITK_MODULES) set(MITK_ROOT_FOLDER_BACKUP "${MITK_ROOT_FOLDER}") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) get_filename_component(MITK_ROOT_FOLDER "${MITK_EXTENSION_DIR}" NAME) set(MITK_MODULES_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Modules") if(EXISTS "${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") set(MITK_MODULES "") include("${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake") foreach(mitk_module ${MITK_MODULES}) add_subdirectory("${MITK_MODULES_EXTENSION_DIR}/${mitk_module}" "Modules/${mitk_module}") endforeach() endif() set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) endforeach() set(MITK_ROOT_FOLDER "${MITK_ROOT_FOLDER_BACKUP}") add_subdirectory(Wrapping) set(MITK_DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/Documentation/Doxygen" CACHE PATH "Output directory for doxygen generated documentation.") if(MITK_USE_BLUEBERRY) include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") mitkFunctionWhitelistPlugins(MITK MITK_PLUGINS) set(mitk_plugins_fullpath "") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() set(MITK_PLUGIN_REGEX_LIST "") foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Plugins") if(EXISTS "${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") set(MITK_PLUGINS "") include("${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath "${MITK_PLUGINS_EXTENSION_DIR}/${mitk_plugin}") endforeach() endif() endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() if(MITK_BUILD_EXAMPLES) include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake") set(mitk_example_plugins_fullpath ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb MITK_PLUGIN_REGEX_LIST OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options set(mitk_apps_fullpath "") foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) if(${option_name}) list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}") endif() endforeach() endif() endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- set(MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS) set(MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS) set(MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS "${MITK_DOXYGEN_ADDITIONAL_INPUT_DIRS} \"${MITK_EXTENSION_DIR}\"") set(MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS "${MITK_DOXYGEN_ADDITIONAL_IMAGE_PATHS} \"${MITK_EXTENSION_DIR}\"") # MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS should be modified by MITK extensions as needed endforeach() if(DOXYGEN_FOUND) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_DOCUMENTATION_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Documentation") file(GLOB MITK_DOCUMENTATION_EXTENSION_FILES CONFIGURE_DEPENDS "${MITK_DOCUMENTATION_EXTENSION_DIR}/*.cmake") foreach(doc_file ${MITK_DOCUMENTATION_EXTENSION_FILES}) include("${doc_file}") endforeach() endforeach() # Transform list of example paths into space-separated string of quoted paths unset(example_paths) foreach(example_path ${MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS}) set(example_paths "${example_paths} \"${example_path}\"") endforeach() set(MITK_DOXYGEN_ADDITIONAL_EXAMPLE_PATHS "${example_paths}") add_subdirectory(Documentation) endif() #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) set(ALL_MITK_APPS "") set(activated_apps_no 0) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") set(MITK_APPS "") include("${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake") foreach(mitk_app ${MITK_APPS}) string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) list(APPEND ALL_MITK_APPS "${MITK_EXTENSION_DIR}/Applications/${directory_name}^^${option_name}^^${executable_name}") if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() endif() endforeach() list(LENGTH ALL_MITK_APPS app_count) if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${ALL_MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${target_dir}/CPackOptions.cmake") include("${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${target_dir}/CPackConfig.cmake") configure_file(${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${executable_name}") endif() endforeach() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- # ---------------- Export targets ----------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() get_target_property(deprecated_module ${target_to_export} MITK_MODULE_DEPRECATED_SINCE) if(deprecated_module) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_MODULE_DEPRECATED_SINCE \"${deprecated_module}\")") endif() endforeach() # ---------------- External projects ----------------- get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) set(MITK_CONFIG_EXTERNAL_PROJECTS ) #string(REPLACE "^^" ";" _mitk_external_projects ${MITK_EXTERNAL_PROJECTS}) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} set(MITK_USE_${ep} ${MITK_USE_${ep}}) set(MITK_${ep}_DIR \"${${ep}_DIR}\") set(MITK_${ep}_COMPONENTS ${_components}) ") endforeach() foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(_components) set(_components_arg COMPONENTS \${_components}) else() set(_components_arg) endif() if(_package) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} if(MITK_USE_${ep}) set(${ep}_DIR \${MITK_${ep}_DIR}) if(MITK_${ep}_COMPONENTS) mitkMacroFindDependency(${_package} COMPONENTS \${MITK_${ep}_COMPONENTS}) else() mitkMacroFindDependency(${_package}) endif() endif()") endif() endforeach() # ---------------- Tools ----------------- configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) # ---------------- Configure files ----------------- configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) if(MSVC AND TARGET MitkWorkbench) set_directory_properties(PROPERTIES VS_STARTUP_PROJECT MitkWorkbench) endif() foreach(MITK_EXTENSION_DIR ${MITK_ABSOLUTE_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR "${MITK_EXTENSION_DIR}/Applications") if(EXISTS "${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt") add_subdirectory("${MITK_APPLICATIONS_EXTENSION_DIR}" "Applications") endif() endforeach() #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Modules/REST/test/CMakeLists.txt b/Modules/REST/test/CMakeLists.txt index e42e23ad3f..2e7b24b2f7 100644 --- a/Modules/REST/test/CMakeLists.txt +++ b/Modules/REST/test/CMakeLists.txt @@ -1,2 +1,2 @@ mitk_create_module_tests() -set_tests_properties(mitkRESTClientTest mitkRESTServerTest PROPERTIES RUN_SERIAL TRUE) +set_tests_properties(mitkRESTClientTest mitkRESTServerTest mitkRESTServerHttpLibTest PROPERTIES RUN_SERIAL TRUE) diff --git a/Modules/REST/test/files.cmake b/Modules/REST/test/files.cmake index 7299b4bcce..57628aaee3 100644 --- a/Modules/REST/test/files.cmake +++ b/Modules/REST/test/files.cmake @@ -1,4 +1,5 @@ set(MODULE_TESTS + mitkRESTServerHttpLibTest.cpp mitkRESTClientTest.cpp mitkRESTServerTest.cpp ) \ No newline at end of file diff --git a/Modules/REST/test/mitkRESTServerHttpLibTest.cpp b/Modules/REST/test/mitkRESTServerHttpLibTest.cpp new file mode 100644 index 0000000000..8d0f63f3e2 --- /dev/null +++ b/Modules/REST/test/mitkRESTServerHttpLibTest.cpp @@ -0,0 +1,106 @@ +/*============================================================================ + +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 + +using namespace std::chrono_literals; + +class mitkRESTServerHttpLibTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkRESTServerHttpLibTestSuite); + MITK_TEST(OpenListener_Succeed); + MITK_TEST(OpenListenerGetRequestSamePath_ReturnExpectedJSON); + CPPUNIT_TEST_SUITE_END(); + std::vector> m_ft; + +public: + void OpenListener_Succeed() + { + httplib::Server svr; + svr.Get("/get", + [](const httplib::Request &, httplib::Response &res) + { res.set_content("Hello World from MITK!", "text/plain"); }); + std::future ft = std::async(std::launch::async, [&]() { svr.listen("localhost", 8080); }); + while (!svr.is_running()) + { + std::this_thread::sleep_for(100ms); + } + CPPUNIT_ASSERT_MESSAGE("Server is running", svr.is_running()); + svr.stop(); + } + + void OpenListenerGetRequestSamePath_ReturnExpectedJSON() + { + httplib::Server svr; + std::map msgdb; + msgdb[0] = "hello_MITK"; + + svr.Get(R"(/msg/(\d+))", + [&](const httplib::Request &req, httplib::Response &res) + { + auto n = req.matches[1]; + nlohmann::json jRes; + try + { + jRes["id"] = std::stoi(n); + jRes["msg"] = msgdb[std::stoi(n)]; + + res.set_content(jRes.dump(), "application/json"); + } + catch (const std::exception &) + { + res.set_content("Cannot find the requested message.", "text/plain"); + } + }); + std::future ft = std::async(std::launch::async, [&]() { svr.listen("localhost", 8080); }); + while (!svr.is_running()) + { + std::this_thread::sleep_for(100ms); + } + httplib::Client cli("http://localhost:8080"); + try + { + if (auto response = cli.Get("/msg/0")) + { + if (response->status == 200) + { + auto js = nlohmann::json::parse(response->body); + std::string msg = js["msg"]; + CPPUNIT_ASSERT_MESSAGE("Result is the expected JSON value", msg == msgdb[0]); + } + else + { + CPPUNIT_ASSERT_MESSAGE("A connection cannot be established", false); + } + } + else + { + CPPUNIT_ASSERT_MESSAGE("A connection cannot be established", false); + } + } + catch (const std::exception &e) + { + svr.stop(); + CPPUNIT_ASSERT_MESSAGE(e.what(), false); + } + svr.stop(); + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkRESTServerHttpLib) diff --git a/Modules/Segmentation/CMakeLists.txt b/Modules/Segmentation/CMakeLists.txt index 1708ede8d0..eea51f14ae 100644 --- a/Modules/Segmentation/CMakeLists.txt +++ b/Modules/Segmentation/CMakeLists.txt @@ -1,11 +1,11 @@ mitk_create_module( INCLUDE_DIRS Algorithms Controllers DataManagement Interactions Rendering SegmentationUtilities/BooleanOperations SegmentationUtilities/MorphologicalOperations DEPENDS MitkAlgorithmsExt MitkSurfaceInterpolation MitkGraphAlgorithms MitkContourModel MitkMultilabel MitkBoundingShape PACKAGE_DEPENDS - PUBLIC ITK|QuadEdgeMesh+RegionGrowing + PUBLIC ITK|QuadEdgeMesh+RegionGrowing httplib PRIVATE ITK|LabelMap+MathematicalMorphology VTK|ImagingGeneral TARGET_DEPENDS PRIVATE GrowCut ) add_subdirectory(cmdapps) add_subdirectory(Testing) diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp new file mode 100644 index 0000000000..20427becea --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.cpp @@ -0,0 +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. + +============================================================================*/ + +// MITK +#include "mitkMonaiLabel2DTool.h" +#include +#include +#include + +// us +#include +#include +#include +#include +#include + +namespace mitk +{ + MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, MonaiLabel2DTool, "MonaiLabel2D"); +} + +void mitk::MonaiLabel2DTool::Activated() +{ + Superclass::Activated(); + this->SetLabelTransferScope(LabelTransferScope::AllLabels); +} + +const char **mitk::MonaiLabel2DTool::GetXPM() const +{ + return nullptr; +} + +us::ModuleResource mitk::MonaiLabel2DTool::GetIconResource() const +{ + us::Module *module = us::GetModuleContext()->GetModule(); + us::ModuleResource resource = module->GetResource("AI.svg"); + return resource; +} + +const char *mitk::MonaiLabel2DTool::GetName() const +{ + return "MONAI Label 2D"; +} + +void mitk::MonaiLabel2DTool::WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const +{ + auto extendedImage = Image::New(); + std::array dim = {inputAtTimeStep->GetDimension(0), inputAtTimeStep->GetDimension(1), 1}; + mitk::PixelType pt = inputAtTimeStep->GetPixelType(); + extendedImage->Initialize(pt, 3, dim.data()); + + ImageReadAccessor readAccessor(inputAtTimeStep); + extendedImage->SetVolume(readAccessor.GetData()); + + IOUtil::Save(extendedImage.GetPointer(), inputImagePath); +} + +void mitk::MonaiLabel2DTool::WriteBackResults(LabelSetImage *previewImage, + LabelSetImage *segResults, + TimeStepType timeStep) const + { + if (segResults->GetTimeGeometry()->CountTimeSteps() > 1) + { + mitkThrow() << "Invalid time geometry found while writing back segmentation. " + " Expected static segmentation output from model."; + } + mitk::SegTool2D::WriteSliceToVolume(previewImage, this->GetWorkingPlaneGeometry(), segResults, timeStep, false); + } diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h new file mode 100644 index 0000000000..51ca9e40ed --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel2DTool.h @@ -0,0 +1,53 @@ +/*============================================================================ + +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 mitkMonaiLabel2DTool_h +#define mitkMonaiLabel2DTool_h + +#include "mitkMonaiLabelTool.h" +#include + +namespace us +{ + class ModuleResource; +} + +namespace mitk +{ + /** + \brief MonaiLabel segmentation 2D tool. + + \ingroup ToolManagerEtAl + \sa mitk::Tool + \sa QmitkInteractiveSegmentation + + */ + class MITKSEGMENTATION_EXPORT MonaiLabel2DTool : public MonaiLabelTool + { + public: + mitkClassMacro(MonaiLabel2DTool, MonaiLabelTool); + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + + const char *GetName() const override; + const char **GetXPM() const override; + us::ModuleResource GetIconResource() const override; + + void Activated() override; + void WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const override; + void WriteBackResults(LabelSetImage *previewImage, LabelSetImage *segResults, TimeStepType timeStep) const override; + + protected: + MonaiLabel2DTool() = default; + ~MonaiLabel2DTool() = default; + }; +} +#endif diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp new file mode 100644 index 0000000000..b83a3363fa --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.cpp @@ -0,0 +1,67 @@ +/*============================================================================ + +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 "mitkMonaiLabel3DTool.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace mitk +{ + MITK_TOOL_MACRO(MITKSEGMENTATION_EXPORT, MonaiLabel3DTool, "MonaiLabel3D"); +} + +void mitk::MonaiLabel3DTool::Activated() +{ + Superclass::Activated(); + this->SetLabelTransferScope(LabelTransferScope::AllLabels); +} + +const char **mitk::MonaiLabel3DTool::GetXPM() const +{ + return nullptr; +} + +us::ModuleResource mitk::MonaiLabel3DTool::GetIconResource() const +{ + us::Module *module = us::GetModuleContext()->GetModule(); + us::ModuleResource resource = module->GetResource("AI.svg"); + return resource; +} + +const char *mitk::MonaiLabel3DTool::GetName() const +{ + return "MONAI Label 3D"; +} + +void mitk::MonaiLabel3DTool::WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const +{ + IOUtil::Save(inputAtTimeStep, inputImagePath); +} + +void mitk::MonaiLabel3DTool::WriteBackResults(LabelSetImage *previewImage, + LabelSetImage *segResults, + TimeStepType timeStep) const +{ + if (segResults->GetTimeGeometry()->CountTimeSteps() > 1) + { + mitkThrow() << "Invalid time geometry found while writing back segmentation. " + " Expected static segmentation output from model."; + } + mitk::ImageReadAccessor newMitkImgAcc(segResults); + previewImage->SetVolume(newMitkImgAcc.GetData(), timeStep); +} diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h new file mode 100644 index 0000000000..31452cf166 --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkMonaiLabel3DTool.h @@ -0,0 +1,52 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ +#ifndef mitkMonaiLabel3DTool_h +#define mitkMonaiLabel3DTool_h + +#include "mitkMonaiLabelTool.h" +#include + +namespace us +{ + class ModuleResource; +} + +namespace mitk +{ + /** + \brief MonaiLabel segmentation 3D tool. + + \ingroup Interaction + \ingroup ToolManagerEtAl + + \warning Only to be instantiated by mitk::ToolManager. + */ + class MITKSEGMENTATION_EXPORT MonaiLabel3DTool : public MonaiLabelTool + { + public: + mitkClassMacro(MonaiLabel3DTool, MonaiLabelTool); + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + + const char *GetName() const override; + const char **GetXPM() const override; + us::ModuleResource GetIconResource() const override; + void Activated() override; + void WriteImage(const Image *inputAtTimeStep, const std::string &inputImagePath) const override; + void WriteBackResults(LabelSetImage *previewImage, LabelSetImage *segResults, TimeStepType timeStep) const override; + + protected: + MonaiLabel3DTool() = default; + ~MonaiLabel3DTool() = default; + }; +} // namespace mitk +#endif diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp new file mode 100644 index 0000000000..27ce24aa9e --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.cpp @@ -0,0 +1,660 @@ +/*============================================================================ + +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 "mitkMonaiLabelTool.h" + +#ifndef CPPHTTPLIB_OPENSSL_SUPPORT +#define CPPHTTPLIB_OPENSSL_SUPPORT +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include "mitkImageAccessByItk.h" + +mitk::MonaiLabelTool::MonaiLabelTool() : SegWithPreviewTool(true, "PressMoveReleaseAndPointSetting") +{ + this->ResetsToEmptyPreviewOn(); + this->IsTimePointChangeAwareOff(); +} + +mitk::MonaiLabelTool::~MonaiLabelTool() +{ + std::filesystem::remove_all(this->GetTempDir()); +} + +void mitk::MonaiLabelTool::ConnectActionsAndFunctions() +{ + CONNECT_FUNCTION("ShiftSecondaryButtonPressed", OnAddNegativePoint); + CONNECT_FUNCTION("ShiftPrimaryButtonPressed", OnAddPositivePoint); + CONNECT_FUNCTION("DeletePoint", OnDelete); +} + +void mitk::MonaiLabelTool::Activated() +{ + Superclass::Activated(); + m_PointSetPositive = mitk::PointSet::New(); + m_PointSetNodePositive = mitk::DataNode::New(); + m_PointSetNodePositive->SetData(m_PointSetPositive); + m_PointSetNodePositive->SetName(std::string(this->GetName()) + "_PointSetPositive"); + m_PointSetNodePositive->SetBoolProperty("helper object", true); + m_PointSetNodePositive->SetColor(0.0, 1.0, 0.0); + m_PointSetNodePositive->SetVisibility(true); + m_PointSetNodePositive->SetProperty("Pointset.2D.shape", + mitk::PointSetShapeProperty::New(mitk::PointSetShapeProperty::CIRCLE)); + m_PointSetNodePositive->SetProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(true)); + this->GetDataStorage()->Add(m_PointSetNodePositive, this->GetToolManager()->GetWorkingData(0)); + + m_PointSetNegative = mitk::PointSet::New(); + m_PointSetNodeNegative = mitk::DataNode::New(); + m_PointSetNodeNegative->SetData(m_PointSetNegative); + m_PointSetNodeNegative->SetName(std::string(this->GetName()) + "_PointSetNegative"); + m_PointSetNodeNegative->SetBoolProperty("helper object", true); + m_PointSetNodeNegative->SetColor(1.0, 0.0, 0.0); + m_PointSetNodeNegative->SetVisibility(true); + m_PointSetNodeNegative->SetProperty("Pointset.2D.shape", + mitk::PointSetShapeProperty::New(mitk::PointSetShapeProperty::CIRCLE)); + m_PointSetNodeNegative->SetProperty("Pointset.2D.fill shape", mitk::BoolProperty::New(true)); + this->GetDataStorage()->Add(m_PointSetNodeNegative, this->GetToolManager()->GetWorkingData(0)); +} + +void mitk::MonaiLabelTool::Deactivated() +{ + this->ClearSeeds(); + this->GetDataStorage()->Remove(m_PointSetNodePositive); + this->GetDataStorage()->Remove(m_PointSetNodeNegative); + m_PointSetNodePositive = nullptr; + m_PointSetNodeNegative = nullptr; + m_PointSetPositive = nullptr; + m_PointSetNegative = nullptr; + Superclass::Deactivated(); +} + +void mitk::MonaiLabelTool::UpdatePrepare() +{ + Superclass::UpdatePrepare(); + auto preview = this->GetPreviewSegmentation(); + preview->RemoveLabels(preview->GetAllLabelValues()); +} + +void mitk::MonaiLabelTool::OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent) +{ + if (m_RequestParameters->model.IsInteractive()) + { + if (m_RequestParameters->model.Is2D() && ((nullptr == this->GetWorkingPlaneGeometry()) || + !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()), + *(this->GetWorkingPlaneGeometry())))) + { + this->ClearSeeds(); + this->SetWorkingPlaneGeometry(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()->Clone()); + this->ResetPreviewContent(); + } + if (!this->IsUpdating() && m_PointSetPositive.IsNotNull()) + { + const auto positionEvent = dynamic_cast(interactionEvent); + if (positionEvent != nullptr) + { + m_PointSetPositive->InsertPoint(m_PointSetCount, positionEvent->GetPositionInWorld()); + ++m_PointSetCount; + this->UpdatePreview(); + } + } + } +} + +void mitk::MonaiLabelTool::OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent) +{ + if ("deepgrow" != m_RequestParameters->model.type) + { + return; + } + if (m_RequestParameters->model.Is2D() && (m_PointSetPositive->GetSize() == 0 || + nullptr == this->GetWorkingPlaneGeometry() || + !mitk::Equal(*(interactionEvent->GetSender()->GetCurrentWorldPlaneGeometry()), + *(this->GetWorkingPlaneGeometry())))) + { + return; + } + if (!this->IsUpdating() && m_PointSetNegative.IsNotNull()) + { + const auto positionEvent = dynamic_cast(interactionEvent); + if (positionEvent != nullptr) + { + m_PointSetNegative->InsertPoint(m_PointSetCount, positionEvent->GetPositionInWorld()); + m_PointSetCount++; + this->UpdatePreview(); + } + } +} + +void mitk::MonaiLabelTool::OnDelete(StateMachineAction *, InteractionEvent *) +{ + if (!this->IsUpdating() && m_PointSetPositive.IsNotNull()) + { + PointSet::Pointer removeSet = m_PointSetPositive; + itk::IdentifierType maxId = 0; + if (m_PointSetPositive->GetSize() > 0) + { + maxId = m_PointSetPositive->GetMaxId().Index(); + } + if (m_PointSetNegative->GetSize() > 0 && (maxId < m_PointSetNegative->GetMaxId().Index())) + { + removeSet = m_PointSetNegative; + } + removeSet->RemovePointAtEnd(0); + --m_PointSetCount; + this->UpdatePreview(); + } +} + +void mitk::MonaiLabelTool::ClearPicks() +{ + this->ClearSeeds(); + this->UpdatePreview(); +} + +bool mitk::MonaiLabelTool::HasPicks() const +{ + return this->m_PointSetPositive.IsNotNull() && this->m_PointSetPositive->GetSize() > 0; +} + +void mitk::MonaiLabelTool::ClearSeeds() +{ + if (this->m_PointSetPositive.IsNotNull()) + { + m_PointSetCount -= m_PointSetPositive->GetSize(); + this->m_PointSetPositive = mitk::PointSet::New(); // renew pointset + this->m_PointSetNodePositive->SetData(this->m_PointSetPositive); + } + if (this->m_PointSetNegative.IsNotNull()) + { + m_PointSetCount -= m_PointSetNegative->GetSize(); + this->m_PointSetNegative = mitk::PointSet::New(); // renew pointset + this->m_PointSetNodeNegative->SetData(this->m_PointSetNegative); + } +} + +bool mitk::MonaiLabelTool::IsMonaiServerOn(const std::string &hostName, const int &port) const +{ + httplib::SSLClient cli(hostName, port); + cli.enable_server_certificate_verification(false); + while (cli.is_socket_open()); + return cli.Get("/info/"); +} + +namespace mitk +{ + // Converts the json GET response from the MonaiLabel server to MonaiAppMetadata object. + void from_json(const nlohmann::json &jsonObj, mitk::MonaiAppMetadata &appData) + { + jsonObj["name"].get_to(appData.name); + jsonObj["description"].get_to(appData.description); + jsonObj["labels"].get_to(appData.labels); + auto modelJsonMap = jsonObj["models"].get>(); + for (const auto &[_name, _jsonObj] : modelJsonMap) + { + if (_jsonObj.is_discarded() || !_jsonObj.is_object()) + { + MITK_ERROR << "Could not parse JSON object."; + } + mitk::MonaiModelInfo modelInfo; + modelInfo.name = _name; + try + { + auto labels = _jsonObj["labels"].get>(); + modelInfo.labels = labels; + } + catch (const std::exception &) + { + auto labels = _jsonObj["labels"].get>(); + for (const auto &label : labels) + { + modelInfo.labels[label] = -1; // Hardcode -1 as label id + } + } + _jsonObj["type"].get_to(modelInfo.type); + _jsonObj["dimension"].get_to(modelInfo.dimension); + _jsonObj["description"].get_to(modelInfo.description); + appData.models.push_back(modelInfo); + } + } +} + +namespace +{ + /** + * @brief Returns boundary string from Httplib response. + */ + std::string GetBoundaryString(const httplib::Result &response) + { + httplib::Headers headers = response->headers; + std::string contentType = headers.find("content-type")->second; + std::string delimiter = "boundary="; + std::string boundaryString = + contentType.substr(contentType.find(delimiter) + delimiter.length(), std::string::npos); + boundaryString.insert(0, "--"); + return boundaryString; + } + + /** + * @brief Returns image data string from monai label overall response. + */ + std::string GetResponseImageString(const std::string &imagePart) + { + std::string contentTypeStream = "Content-Type: application/octet-stream"; + size_t ctPos = imagePart.find(contentTypeStream) + contentTypeStream.length(); + std::string imageDataPart = imagePart.substr(ctPos); + std::string whitespaces = " \n\r"; + imageDataPart.erase(0, imageDataPart.find_first_not_of(whitespaces)); // clean up + return imageDataPart; + } + + /** + * @brief Helper function to get the Parts of the POST response. + */ + std::vector GetPartsBetweenBoundary(const std::string &body, const std::string &boundary) + { + std::vector retVal; + std::string master = body; + size_t boundaryPos = master.find(boundary); + size_t beginPos = 0; + while (boundaryPos != std::string::npos) + { + std::string part = master.substr(beginPos, boundaryPos); + if (!part.empty()) + { + retVal.push_back(part); + } + master.erase(beginPos, boundaryPos + boundary.length()); + boundaryPos = master.find(boundary); + } + return retVal; + } + + /** + * @brief Applies the give std::map lookup table on the preview segmentation LabelSetImage. + */ + void MapLabelsToSegmentation(const mitk::LabelSetImage *source, + mitk::LabelSetImage *dest, + const std::map &labelMap) + { + if (labelMap.empty()) + { + auto label = mitk::LabelSetImageHelper::CreateNewLabel(dest, "object"); + label->SetValue(1); + dest->AddLabel(label, false); + return; + } + std::map flippedLabelMap; + for (auto const &[key, val] : labelMap) + { + flippedLabelMap[val] = key; + } + auto lookupTable = mitk::LookupTable::New(); + lookupTable->SetType(mitk::LookupTable::LookupTableType::MULTILABEL); + for (auto const &[key, val] : flippedLabelMap) + { + if (source->ExistLabel(key, source->GetActiveLayer())) + { + auto label = mitk::Label::New(key, val); + std::array lookupTableColor; + lookupTable->GetColor(key, lookupTableColor.data()); + mitk::Color color; + color.SetRed(lookupTableColor[0]); + color.SetGreen(lookupTableColor[1]); + color.SetBlue(lookupTableColor[2]); + label->SetColor(color); + dest->AddLabel(label, false); + } + else + { + MITK_INFO << "Label not found for " << val; + } + } + } + + template + void ITKWindowing(const itk::Image *inputImage, + mitk::Image *mitkImage, mitk::ScalarType min, mitk::ScalarType max) + { + typedef itk::Image ImageType; + typedef itk::IntensityWindowingImageFilter IntensityFilterType; + typename IntensityFilterType::Pointer filter = IntensityFilterType::New(); + filter->SetInput(inputImage); + filter->SetWindowMinimum(min); + filter->SetWindowMaximum(max); + filter->SetOutputMinimum(min); + filter->SetOutputMaximum(max); + filter->Update(); + + mitkImage->SetImportVolume( + (void *)(filter->GetOutput()->GetPixelContainer()->GetBufferPointer()), 0, 0, mitk::Image::ManageMemory); + filter->GetOutput()->GetPixelContainer()->ContainerManageMemoryOff(); + } +} + +mitk::Image::Pointer mitk::MonaiLabelTool::ApplyLevelWindowEffect(const Image *inputAtTimeStep) const +{ + mitk::LevelWindow levelWindow; + this->GetToolManager()->GetReferenceData(0)->GetLevelWindow(levelWindow); + auto filteredImage = mitk::Image::New(); + filteredImage->Initialize(inputAtTimeStep); + AccessByItk_n(inputAtTimeStep, + ::ITKWindowing, // apply level window filter + (filteredImage, levelWindow.GetLowerWindowBound(), levelWindow.GetUpperWindowBound())); + return filteredImage; +} + +void mitk::MonaiLabelTool::DoUpdatePreview(const Image *inputAtTimeStep, + const Image * /*oldSegAtTimeStep*/, + LabelSetImage *previewImage, + TimeStepType timeStep) +{ + if (nullptr == m_RequestParameters || (m_RequestParameters->model.IsInteractive() && !this->HasPicks())) + { + this->ResetPreviewContentAtTimeStep(timeStep); + RenderingManager::GetInstance()->ForceImmediateUpdateAll(); + return; + } + const std::string &hostName = m_RequestParameters->hostName; + const int port = m_RequestParameters->port; + if (!IsMonaiServerOn(hostName, port)) + { + mitkThrow() << m_SERVER_503_ERROR_TEXT; + } + if (this->m_TempDir.empty()) + { + this->SetTempDir(IOUtil::CreateTemporaryDirectory("mitk-XXXXXX")); + } + + std::string inputImagePath, outputImagePath; + std::tie(inputImagePath, outputImagePath) = this->CreateTempDirs(m_TEMPLATE_FILENAME); + + try + { + mitk::Image::Pointer filteredImage = this->ApplyLevelWindowEffect(inputAtTimeStep); + this->WriteImage(filteredImage, inputImagePath); + this->PostInferRequest(hostName, port, inputImagePath, outputImagePath, inputAtTimeStep->GetGeometry()); + auto outputImage = IOUtil::Load(outputImagePath); + auto outputBuffer = mitk::LabelSetImage::New(); + outputBuffer->InitializeByLabeledImage(outputImage); + std::map labelMap; // empty map + if (m_RequestParameters->model.IsInteractive()) + { + this->SetLabelTransferMode(LabelTransferMode::MapLabel); + this->SetSelectedLabels({MASK_VALUE}); + } + else + { + outputBuffer->SetGeometry(inputAtTimeStep->GetGeometry()); + labelMap = m_ResultMetadata["label_names"]; + this->SetLabelTransferMode(LabelTransferMode::AddLabel); + } + ::MapLabelsToSegmentation(outputBuffer, previewImage, labelMap); + this->WriteBackResults(previewImage, outputBuffer.GetPointer(), timeStep); + MonaiStatusEvent.Send(true); + } + catch (const mitk::Exception &e) + { + MITK_ERROR << e.GetDescription(); + mitkThrow() << e.GetDescription(); + MonaiStatusEvent.Send(false); + } +} + +void mitk::MonaiLabelTool::FetchOverallInfo(const std::string &hostName, const int &port) +{ + m_InfoParameters.reset(); + if (!IsMonaiServerOn(hostName, port)) + { + Tool::ErrorMessage.Send(m_SERVER_503_ERROR_TEXT); + mitkThrow() << m_SERVER_503_ERROR_TEXT; + } + httplib::SSLClient cli(hostName, port); + cli.enable_server_certificate_verification(false); + if (auto response = cli.Get("/info/")) + { + if (response->status == 200) + { + auto jsonObj = nlohmann::json::parse(response->body); + if (jsonObj.is_discarded() || !jsonObj.is_object()) + { + MITK_ERROR << "Could not parse response from MONAILabel server as JSON object!"; + return; + } + auto appData = jsonObj.template get(); + m_InfoParameters = std::make_unique(appData); + if (nullptr != m_InfoParameters) + { + m_InfoParameters->hostName = hostName; + m_InfoParameters->port = port; + } + } + } + else + { + Tool::ErrorMessage.Send(httplib::to_string(response.error()) + " error occured."); + } +} + +void mitk::MonaiLabelTool::PostInferRequest(const std::string &hostName, + const int &port, + const std::string &filePath, + const std::string &outFile, + const mitk::BaseGeometry *baseGeometry) +{ + std::string &modelName = m_RequestParameters->model.name; // Get this from args as well. + std::string postPath = "/infer/"; // make this separate class of constants + postPath.append(modelName); + std::ifstream input(filePath, std::ios::binary); + if (!input) + { + MITK_WARN << "could not read file to POST"; + } + std::stringstream buffer_lf_img; + buffer_lf_img << input.rdbuf(); + input.close(); + httplib::MultipartFormDataItems items; + if (m_RequestParameters->model.IsInteractive()) + { + std::string foreground = this->ConvertPointsAsListString(baseGeometry, m_PointSetPositive); + std::string background = this->ConvertPointsAsListString(baseGeometry, m_PointSetNegative); + std::stringstream paramString; + paramString << "{" + << "\"foreground\":" << foreground + << ",\"background\":" << background + << "}"; + MITK_DEBUG << paramString.str(); + items.push_back({"params", paramString.str(), "", ""}); + } + else // Auto models + { + items.push_back({"params", "{\"restore_label_idx\": true}", "", ""}); + } + items.push_back({"file", buffer_lf_img.str(), "post_from_mitk.nii.gz", "application/octet-stream"}); + httplib::SSLClient cli(hostName, port); + cli.set_read_timeout(60); // arbitary 1 minute time-out to avoid corner cases. + cli.enable_server_certificate_verification(false); + if (auto response = cli.Post(postPath, items)) + { + if (response->status == 200) + { + // Find boundary + std::string boundaryString = ::GetBoundaryString(response); + MITK_DEBUG << "boundary hash: " << boundaryString; + + // Parse metadata JSON + std::string resBody = response->body; + std::vector multiPartResponse = ::GetPartsBetweenBoundary(resBody, boundaryString); + + std::string metaData = multiPartResponse[0]; + std::string contentTypeJson = "Content-Type: application/json"; + size_t ctPos = metaData.find(contentTypeJson) + contentTypeJson.length(); + std::string metaDataPart = metaData.substr(ctPos); + MITK_DEBUG << metaDataPart; + auto jsonObj = nlohmann::json::parse(metaDataPart); + if (jsonObj.is_discarded() || !jsonObj.is_object()) + { + MITK_ERROR << "Could not parse response from MONAILabel server as JSON object!"; + return; + } + else // use the metadata + { + m_ResultMetadata = jsonObj; + } + // Parse response image string + std::string imagePart = multiPartResponse[1]; + std::string imageData = ::GetResponseImageString(imagePart); + std::ofstream output(outFile, std::ios::out | std::ios::app | std::ios::binary); + output << imageData; + output.unsetf(std::ios::skipws); + output.flush(); + } + else + { + auto err = response.error(); + MITK_ERROR << "An HTTP POST error: " << httplib::to_string(err) << " occured."; + mitkThrow() << "An HTTP POST error: " << httplib::to_string(err) << " occured."; + } + } +} + +const std::vector mitk::MonaiLabelTool::GetAutoSegmentationModels(const int dim) const +{ + std::vector autoModels; + if (nullptr != m_InfoParameters) + { + for (mitk::MonaiModelInfo &model : m_InfoParameters->models) + { + if (m_AUTO_SEG_TYPE_NAME.find(model.type) != m_AUTO_SEG_TYPE_NAME.end() && (!(dim > -1) || + model.dimension == dim)) + { + autoModels.push_back(model); + } + } + } + return autoModels; +} + +const std::vector mitk::MonaiLabelTool::GetInteractiveSegmentationModels(const int dim) const +{ + std::vector interactiveModels; + if (nullptr != m_InfoParameters) + { + for (mitk::MonaiModelInfo &model : m_InfoParameters->models) + { + if (m_INTERACTIVE_SEG_TYPE_NAME.find(model.type) != m_INTERACTIVE_SEG_TYPE_NAME.end() && + (!(dim > -1) || model.dimension == dim)) + { + interactiveModels.push_back(model); + } + } + } + return interactiveModels; +} + +const std::vector mitk::MonaiLabelTool::GetScribbleSegmentationModels(const int dim) const +{ + std::vector scribbleModels; + if (nullptr != m_InfoParameters) + { + for (mitk::MonaiModelInfo &model : m_InfoParameters->models) + { + if (m_SCRIBBLE_SEG_TYPE_NAME.find(model.type) != m_SCRIBBLE_SEG_TYPE_NAME.end() && + (!(dim > -1) || model.dimension == dim)) + { + scribbleModels.push_back(model); + } + } + } + return scribbleModels; +} + +mitk::MonaiModelInfo mitk::MonaiLabelTool::GetModelInfoFromName(const std::string modelName) const +{ + if (nullptr == m_InfoParameters) + { + mitkThrow() << "No model information found."; + } + mitk::MonaiModelInfo retVal; + for (mitk::MonaiModelInfo &model : m_InfoParameters->models) + { + if (model.name == modelName) + { + retVal = model; + break; + } + } + return retVal; +} + +std::pair mitk::MonaiLabelTool::CreateTempDirs(const std::string &filePattern) const +{ + std::string inDir, outDir, inputImagePath, outputImagePath; + inDir = IOUtil::CreateTemporaryDirectory("monai-in-XXXXXX", this->GetTempDir()); + std::ofstream tmpStream; + inputImagePath = IOUtil::CreateTemporaryFile(tmpStream, filePattern, 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("_")); + outDir = IOUtil::CreateTemporaryDirectory("monai-out-XXXXXX", this->GetTempDir()); + outputImagePath = outDir + IOUtil::GetDirectorySeparator() + token + "_000.nii.gz"; + return std::make_pair(inputImagePath, outputImagePath); +} + +std::string mitk::MonaiLabelTool::ConvertPointsAsListString(const mitk::BaseGeometry *baseGeometry, + const PointSet::Pointer pointSet) const +{ + bool is3DMode = nullptr == this->GetWorkingPlaneGeometry(); + MITK_INFO << "No.of points: " << pointSet->GetSize(); + std::stringstream pointsAndLabels; + pointsAndLabels << "["; + mitk::PointSet::PointsConstIterator pointSetIter = pointSet->Begin(); + const char COMMA = ','; + while (pointSetIter != pointSet->End()) + { + mitk::Point3D point3d = pointSetIter.Value(); + if (baseGeometry->IsInside(point3d)) + { + mitk::Point3D index3D; + baseGeometry->WorldToIndex(point3d, index3D); + pointsAndLabels << "["; + pointsAndLabels << static_cast(index3D[0]) << COMMA << static_cast(index3D[1]) << COMMA; + if (is3DMode) + { + pointsAndLabels << static_cast(index3D[2]); + } + else + { + pointsAndLabels << 0; + } + pointsAndLabels << "],"; + } + ++pointSetIter; + } + if (pointsAndLabels.tellp() > 1) + { + pointsAndLabels.seekp(-1, pointsAndLabels.end); // remove last added comma character + } + pointsAndLabels << "]"; + return pointsAndLabels.str(); +} + +const mitk::MonaiAppMetadata *mitk::MonaiLabelTool::GetInfoParameters() const +{ + return m_InfoParameters.get(); +} diff --git a/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h new file mode 100644 index 0000000000..1bf4e4bc0b --- /dev/null +++ b/Modules/Segmentation/Interactions/mitkMonaiLabelTool.h @@ -0,0 +1,264 @@ +/*============================================================================ + +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 mitkMonaiLabelTool_h +#define mitkMonaiLabelTool_h + +#include "mitkSegWithPreviewTool.h" +#include +#include +#include +#include +#include +#include +#include + + +namespace us +{ + class ModuleResource; +} + +namespace mitk +{ + /** + * @brief Struct to hold featured models individual info + * + */ + struct MonaiModelInfo + { + std::string name; + std::string type; + std::unordered_map labels; + int dimension; + std::string description; + std::unordered_map config; + + inline bool operator==(const MonaiModelInfo &rhs) const + { + return (this->name == rhs.name && this->type == rhs.type); // Comparing only name and type, for now. + } + + inline bool IsInteractive() const + { + return ("deepgrow" == type || "deepedit" == type); + } + + inline bool Is2D() const + { + return dimension == 2; + } + }; + + /** + * @brief Struct to store MonaiLabel server metadata including all model infos + * + */ + struct MonaiAppMetadata + { + std::string name; + std::string description; + std::vector labels; + std::string version; + std::string hostName; + int port; + std::string origin; + std::vector models; + }; + + /** + * @brief Request class to pack model and other necessary server information + * from GUI. + * + */ + struct MonaiLabelRequest + { + MonaiModelInfo model; + std::string hostName; + std::string requestLabel; + int port; + + inline bool operator==(const MonaiLabelRequest &rhs) const + { + return (this->model == rhs.model && this->hostName == rhs.hostName && this->port == rhs.port && + this->requestLabel == rhs.requestLabel); + } + }; + + /** + \brief MonaiLabel segmentation tool base class. + + \ingroup Interaction + \ingroup ToolManagerEtAl + + \warning Only to be instantiated by mitk::ToolManager. + */ + class MITKSEGMENTATION_EXPORT MonaiLabelTool : public SegWithPreviewTool + { + public: + mitkClassMacro(MonaiLabelTool, SegWithPreviewTool); + itkCloneMacro(Self); + void Activated() override; + void Deactivated() override; + void UpdatePrepare() override; + + /** + * @brief Method does the GET Rest call to fetch MonaiLabel + * server metadata including all models' info. + */ + void FetchOverallInfo(const std::string &hostName, const int &port); + + /** + * @brief Variable to set selected model's and other data needed + * for the POST call. + */ + std::unique_ptr m_RequestParameters; + + /** + * @brief Get the Auto Segmentation Models info for the given + * dimension. + */ + const std::vector GetAutoSegmentationModels(const int dim = -1) const; + + /** + * @brief Get the Interactive Segmentation Models info for the given + * dimension. + */ + const std::vector GetInteractiveSegmentationModels(const int dim = -1) const; + + /** + * @brief Get the Scribble Segmentation Models info for the given + * dimension. + */ + const std::vector GetScribbleSegmentationModels(const int dim = -1) const; + + /** + * @brief Helper function to get full model info object from model name. + */ + MonaiModelInfo GetModelInfoFromName(const std::string) const; + + itkSetMacro(ModelName, std::string); + itkGetConstMacro(ModelName, std::string); + itkSetMacro(URL, std::string); + itkGetConstMacro(URL, std::string); + itkSetMacro(TempDir, std::string); + itkGetConstMacro(TempDir, std::string); + const MonaiAppMetadata *GetInfoParameters() const; + + + /** + * @brief Clears all picks and updates the preview. + */ + void ClearPicks(); + + /** + * @brief Checks if any point exists in the either of the PointSetPositive + * or PointSetNegative. + */ + bool HasPicks() const; + + Message1 MonaiStatusEvent; + + protected: + MonaiLabelTool(); + ~MonaiLabelTool(); + void DoUpdatePreview(const Image* inputAtTimeStep, const Image* oldSegAtTimeStep, LabelSetImage* previewImage, TimeStepType timeStep) override; + void ConnectActionsAndFunctions() override; + + /** + * @brief Writes image to disk as the tool desires. + * Default implementation does nothing. + */ + virtual void WriteImage(const Image *, const std::string &) const = 0; + + /* + * @brief Add positive seed point action of StateMachine pattern. + */ + virtual void OnAddPositivePoint(StateMachineAction *, InteractionEvent *interactionEvent); + + /* + * @brief Add negative seed point action of StateMachine pattern + */ + virtual void OnAddNegativePoint(StateMachineAction *, InteractionEvent *interactionEvent); + + /* + * @brief Delete action of StateMachine pattern + */ + virtual void OnDelete(StateMachineAction *, InteractionEvent *); + + /* + * @brief Clear all seed points and call UpdatePreview to reset the segmentation Preview + */ + void ClearSeeds(); + + /** + * @brief Get the Points from given pointset as csv string. + * + */ + virtual std::string ConvertPointsAsListString(const mitk::BaseGeometry *baseGeometry, + const PointSet::Pointer pointSet) const; + + /** + * @brief Writes back segmentation results in 3D or 2D shape to preview LabelSetImage. + */ + virtual void WriteBackResults(LabelSetImage *, LabelSetImage *, TimeStepType) const = 0; + + PointSet::Pointer m_PointSetPositive; + PointSet::Pointer m_PointSetNegative; + DataNode::Pointer m_PointSetNodePositive; + DataNode::Pointer m_PointSetNodeNegative; + int m_PointSetCount = 0; + + private: + + /** + * @brief Holds all parameters of the server to serve the UI + * + */ + std::unique_ptr m_InfoParameters; + + /** + * @brief Helper function to create temp directory for writing/reading images + * Returns input and output file names expected as a pair. + */ + std::pair CreateTempDirs(const std::string &filePattern) const; + + /** + * @brief Checks if MonaiLabel server is alive. + */ + bool IsMonaiServerOn(const std::string &hostName, const int &port) const; + + /** + * @brief Applies level window filter on the input image. Current Level window bounds + * are captured from the tool manager. + */ + mitk::Image::Pointer ApplyLevelWindowEffect(const Image *inputAtTimeStep) const; + + /** + * @brief Function to prepare the Rest request and does the POST call. + * Writes the POST responses back to disk. + */ + void PostInferRequest(const std::string &hostName, const int &port, const std::string &filePath, const std::string &outFile, + const mitk::BaseGeometry *baseGeometry); + + std::string m_TempDir; + std::string m_ModelName; + std::string m_URL; + nlohmann::json m_ResultMetadata; + const std::set m_AUTO_SEG_TYPE_NAME = {"segmentation"}; + const std::set m_SCRIBBLE_SEG_TYPE_NAME = {"scribbles"}; + const std::set m_INTERACTIVE_SEG_TYPE_NAME = {"deepgrow"}; // deepedit not supported yet + const std::string m_TEMPLATE_FILENAME = "XXXXXX_000_0000.nii.gz"; + const std::string m_SERVER_503_ERROR_TEXT = "A connection to MonaiLabel server cannot be established."; + const Label::PixelType MASK_VALUE = 1; + }; +} +#endif diff --git a/Modules/Segmentation/Interactions/mitkProcessExecutor.h b/Modules/Segmentation/Interactions/mitkProcessExecutor.h index f67567de25..11df031b0b 100644 --- a/Modules/Segmentation/Interactions/mitkProcessExecutor.h +++ b/Modules/Segmentation/Interactions/mitkProcessExecutor.h @@ -1,116 +1,116 @@ /*============================================================================ 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 mitkProcessExecutor_h #define mitkProcessExecutor_h #include #include #include #include namespace mitk { // Class is adapted from MatchPoint ProcessExecutor class ExternalProcessOutputEvent : public itk::AnyEvent { public: typedef ExternalProcessOutputEvent Self; typedef itk::AnyEvent Superclass; explicit ExternalProcessOutputEvent(const std::string &output = "") : m_Output(output) {} ~ExternalProcessOutputEvent() override {} const char *GetEventName() const override { return "ExternalProcessOutputEvent"; } bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } itk::EventObject *MakeObject() const override { return new Self(m_Output); } std::string GetOutput() const { return m_Output; } private: std::string m_Output; }; #define mitkProcessExecutorEventMacro(classname) \ class classname : public ExternalProcessOutputEvent \ { \ public: \ typedef classname Self; \ typedef ExternalProcessOutputEvent Superclass; \ \ explicit classname(const std::string &output) : Superclass(output) {} \ ~classname() override {} \ \ - virtual const char *GetEventName() const { return #classname; } \ - virtual bool CheckEvent(const ::itk::EventObject *e) const { return dynamic_cast(e); } \ - virtual ::itk::EventObject *MakeObject() const { return new Self(this->GetOutput()); } \ + virtual const char *GetEventName() const override { return #classname; } \ + virtual bool CheckEvent(const ::itk::EventObject *e) const override { return dynamic_cast(e); } \ + virtual ::itk::EventObject *MakeObject() const override { return new Self(this->GetOutput()); } \ }; mitkProcessExecutorEventMacro(ExternalProcessStdOutEvent); mitkProcessExecutorEventMacro(ExternalProcessStdErrEvent); /** * @brief You may register an observer for an ExternalProcessOutputEvent, ExternalProcessStdOutEvent or * ExternalProcessStdErrEvent in order to get notified of any output. * @remark The events will only be invoked if the pipes are NOT(!) shared. By default the pipes are not shared. * */ class MITKSEGMENTATION_EXPORT ProcessExecutor : public itk::Object { public: using Self = ProcessExecutor; using Superclass = ::itk::Object; using Pointer = ::itk::SmartPointer; using ConstPointer = ::itk::SmartPointer; itkTypeMacro(ProcessExecutor, ::itk::Object); itkFactorylessNewMacro(Self); itkSetMacro(SharedOutputPipes, bool); itkGetConstMacro(SharedOutputPipes, bool); using ArgumentListType = std::vector; bool Execute(const std::string &executionPath, const std::string &executableName, ArgumentListType &argumentList); /** * @brief Executes the process. This version assumes that the executable name is the first argument in the argument * list and has already been converted to its OS dependent name via the static convert function of this class. */ virtual bool Execute(const std::string &executionPath, const ArgumentListType &argumentList); int GetExitValue(); static std::string EnsureCorrectOSPathSeparator(const std::string &); static std::string GetOSDependendExecutableName(const std::string &name); void KillProcess(); protected: ProcessExecutor(); ~ProcessExecutor() override; int m_ExitValue; /** * @brief Specifies if the child process should share the output pipes (true) or not (false). * If pipes are not shared the output will be passed by invoking ExternalProcessOutputEvents * @remark The events will only be invoked if the pipes are NOT(!) shared. */ bool m_SharedOutputPipes; private: itksysProcess *m_ProcessID = nullptr; }; } // namespace mitk #endif diff --git a/Modules/Segmentation/files.cmake b/Modules/Segmentation/files.cmake index 31b461d399..fce1bc0f64 100644 --- a/Modules/Segmentation/files.cmake +++ b/Modules/Segmentation/files.cmake @@ -1,117 +1,120 @@ 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/mitkGrowCutSegmentationFilter.cpp Algorithms/mitkImageLiveWireContourModelFilter.cpp Algorithms/mitkImageToContourFilter.cpp Algorithms/mitkManualSegmentationToSurfaceFilter.cpp Algorithms/mitkOtsuSegmentationFilter.cpp Algorithms/mitkSegmentationHelper.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/mitkAddContourTool.cpp Interactions/mitkAutoCropTool.cpp Interactions/mitkSegWithPreviewTool.cpp Interactions/mitkBinaryThresholdBaseTool.cpp Interactions/mitkBinaryThresholdTool.cpp Interactions/mitkBinaryThresholdULTool.cpp Interactions/mitkCloseRegionTool.cpp Interactions/mitkContourModelInteractor.cpp Interactions/mitkContourModelLiveWireInteractor.cpp Interactions/mitkEditableContourTool.cpp Interactions/mitkLiveWireTool2D.cpp Interactions/mitkLassoTool.cpp Interactions/mitkContourTool.cpp Interactions/mitkDrawPaintbrushTool.cpp Interactions/mitkErasePaintbrushTool.cpp Interactions/mitkEraseRegionTool.cpp Interactions/mitkFeedbackContourTool.cpp Interactions/mitkFillRegionBaseTool.cpp Interactions/mitkFillRegionTool.cpp Interactions/mitkGrowCutTool.cpp Interactions/mitkOtsuTool3D.cpp Interactions/mitkPaintbrushTool.cpp Interactions/mitkRegionGrowingTool.cpp Interactions/mitkSegmentationsProcessingTool.cpp Interactions/mitkSegTool2D.cpp Interactions/mitkSubtractContourTool.cpp Interactions/mitkTool.cpp Interactions/mitkToolCommand.cpp Interactions/mitkPickingTool.cpp Interactions/mitknnUnetTool.cpp Interactions/mitkProcessExecutor.cpp Interactions/mitkSegmentAnythingProcessExecutor.cpp + Interactions/mitkMonaiLabelTool.cpp + Interactions/mitkMonaiLabel2DTool.cpp + Interactions/mitkMonaiLabel3DTool.cpp Interactions/mitkTotalSegmentatorTool.cpp Interactions/mitkSegmentAnythingTool.cpp Interactions/mitkMedSAMTool.cpp Interactions/mitkSegmentAnythingPythonService.cpp Rendering/mitkContourMapper2D.cpp Rendering/mitkContourSetMapper2D.cpp Rendering/mitkContourSetVtkMapper3D.cpp Rendering/mitkContourVtkMapper3D.cpp SegmentationUtilities/BooleanOperations/mitkBooleanOperation.cpp SegmentationUtilities/MorphologicalOperations/mitkMorphologicalOperations.cpp #Added from ML Algorithms/mitkSurfaceStampImageFilter.cpp ) set(RESOURCE_FILES Add.svg Add_Cursor.svg AI.svg AI_Cursor.svg Close.svg Close_Cursor.svg Erase.svg Erase_Cursor.svg Fill.svg Fill_Cursor.svg LiveWire.svg LiveWire_Cursor.svg Lasso.svg GrowCut.svg Lasso_Cursor.svg Otsu.svg Paint.svg Paint_Cursor.svg Picking.svg RegionGrowing.svg RegionGrowing_Cursor.svg Subtract.svg Subtract_Cursor.svg Threshold.svg ULThreshold.svg Wipe.svg Wipe_Cursor.svg Interactions/dummy.xml Interactions/EditableContourTool.xml Interactions/PickingTool.xml Interactions/MouseReleaseOnly.xml Interactions/PressMoveRelease.xml Interactions/PressMoveReleaseAndPointSetting.xml Interactions/PressMoveReleaseWithCTRLInversion.xml Interactions/PressMoveReleaseWithCTRLInversionAllMouseMoves.xml Interactions/SegmentationConfig.xml Interactions/SegmentationInteraction.xml Interactions/SegmentationToolsConfig.xml Interactions/ContourModelModificationConfig.xml Interactions/ContourModelModificationInteractor.xml ) diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.cpp new file mode 100644 index 0000000000..43fb118247 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.cpp @@ -0,0 +1,17 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "QmitkMonaiLabel2DToolGUI.h" + +MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkMonaiLabel2DToolGUI, "") + +QmitkMonaiLabel2DToolGUI::QmitkMonaiLabel2DToolGUI(): QmitkMonaiLabelToolGUI(2) {} diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.h new file mode 100644 index 0000000000..9f5bd5c824 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel2DToolGUI.h @@ -0,0 +1,33 @@ +/*============================================================================ + +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 QmitkMonaiLabelTool2DGUI_h +#define QmitkMonaiLabelTool2DGUI_h + +#include +#include "QmitkMonaiLabelToolGUI.h" + +class MITKSEGMENTATIONUI_EXPORT QmitkMonaiLabel2DToolGUI : public QmitkMonaiLabelToolGUI +{ + Q_OBJECT + +public: + mitkClassMacro(QmitkMonaiLabel2DToolGUI, QmitkMonaiLabelToolGUI); + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + +protected: + QmitkMonaiLabel2DToolGUI(); + ~QmitkMonaiLabel2DToolGUI() = default; +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.cpp new file mode 100644 index 0000000000..15857ff290 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.cpp @@ -0,0 +1,17 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "QmitkMonaiLabel3DToolGUI.h" + +MITK_TOOL_GUI_MACRO(MITKSEGMENTATIONUI_EXPORT, QmitkMonaiLabel3DToolGUI, "") + +QmitkMonaiLabel3DToolGUI::QmitkMonaiLabel3DToolGUI(): QmitkMonaiLabelToolGUI(3) {} diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.h new file mode 100644 index 0000000000..20254534d3 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabel3DToolGUI.h @@ -0,0 +1,32 @@ +/*============================================================================ + +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 QmitkMonaiLabelTool3DGUI_h +#define QmitkMonaiLabelTool3DGUI_h + +#include +#include "QmitkMonaiLabelToolGUI.h" + +class MITKSEGMENTATIONUI_EXPORT QmitkMonaiLabel3DToolGUI : public QmitkMonaiLabelToolGUI +{ + Q_OBJECT + +public: + mitkClassMacro(QmitkMonaiLabel3DToolGUI, QmitkMonaiLabelToolGUI); + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + +protected: + QmitkMonaiLabel3DToolGUI(); + ~QmitkMonaiLabel3DToolGUI() = default; +}; +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp new file mode 100644 index 0000000000..256f783b59 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.cpp @@ -0,0 +1,335 @@ +/*============================================================================ + +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 "QmitkMonaiLabelToolGUI.h" + +#include +#include +#include +#include +#include +#include + +namespace +{ + mitk::IPreferences *GetPreferences() + { + auto *preferencesService = mitk::CoreServices::GetPreferencesService(); + return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation"); + } + + const static QString CONFIRM_QUESTION_TEXT = + "Data will be sent to the processing server devoid of any patient information. Are you sure you want continue?"; + + /* Pretested models in MITK and corresponding app */ + const static QMap WHITELISTED_MODELS = { + {"deepgrow_2d", "Radiology"}, + {"deepgrow_3d", "Radiology"}, + {"deepedit_seg", "Radiology"}, + {"localization_vertebra", "Radiology"}, + {"segmentation", "Radiology"}, + {"segmentation_spleen", "Radiology"}, + {"segmentation_vertebra", "Radiology"}, + {"deepgrow_pipeline", "Radiology"}, + {"vertebra_pipeline", "Radiology"}}; + + /* Strictly unsupported models in MITK and corresponding app */ + const static QMap BLACKLISTED_MODELS = { + {"deepedit", "Radiology"}, // interaction type not yet supported + {"localization_spine", "Radiology"}}; // Metadata discrepancy with label names +} + +QmitkMonaiLabelToolGUI::QmitkMonaiLabelToolGUI(int dimension) + : QmitkMultiLabelSegWithPreviewToolGUIBase(), + m_SuperclassEnableConfirmSegBtnFnc(m_EnableConfirmSegBtnFnc), + m_Dimension(dimension) +{ + m_EnableConfirmSegBtnFnc = [this](bool enabled) + { return !m_FirstPreviewComputation ? m_SuperclassEnableConfirmSegBtnFnc(enabled) : false; }; + m_Preferences = GetPreferences(); + m_Preferences->OnPropertyChanged += + mitk::MessageDelegate1( + this, &QmitkMonaiLabelToolGUI::OnPreferenceChangedEvent); +} + +QmitkMonaiLabelToolGUI::~QmitkMonaiLabelToolGUI() +{ + auto tool = this->GetConnectedToolAs(); + if (nullptr != tool) + { + tool->MonaiStatusEvent -= + mitk::MessageDelegate1(this, &QmitkMonaiLabelToolGUI::StatusMessageListener); + } + m_Preferences->OnPropertyChanged -= + mitk::MessageDelegate1( + this, &QmitkMonaiLabelToolGUI::OnPreferenceChangedEvent); +} + +void QmitkMonaiLabelToolGUI::ConnectNewTool(mitk::SegWithPreviewTool *newTool) +{ + Superclass::ConnectNewTool(newTool); + m_FirstPreviewComputation = true; +} + +void QmitkMonaiLabelToolGUI::InitializeUI(QBoxLayout *mainLayout) +{ + m_Controls.setupUi(this); + mainLayout->addLayout(m_Controls.verticalLayout); + + connect(m_Controls.previewButton, SIGNAL(clicked()), this, SLOT(OnPreviewBtnClicked())); + connect(m_Controls.fetchUrl, SIGNAL(clicked()), this, SLOT(OnFetchBtnClicked())); + connect(m_Controls.modelBox, + QOverload::of(&QComboBox::activated), + [=](int index) { OnModelChanged(m_Controls.modelBox->itemText(index)); }); + QIcon refreshIcon = + QmitkStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/view-refresh.svg")); + m_Controls.fetchUrl->setIcon(refreshIcon); + m_Controls.previewButton->setEnabled(false); + Superclass::InitializeUI(mainLayout); +} + +void QmitkMonaiLabelToolGUI::EnableWidgets(bool enabled) +{ + Superclass::EnableWidgets(enabled); +} + +void QmitkMonaiLabelToolGUI::StatusMessageListener(const bool status) +{ + if (!status) + { + return; + } + auto tool = this->GetConnectedToolAs(); + if (nullptr == tool) + { + return; + } + this->SetLabelSetPreview(tool->GetPreviewSegmentation()); + this->ActualizePreviewLabelVisibility(); + m_FirstPreviewComputation = false; +} + +void QmitkMonaiLabelToolGUI::DisplayWidgets(bool enabled) +{ + Superclass::DisplayTransferWidgets(enabled); + m_Controls.previewButton->setVisible(enabled); +} + +void QmitkMonaiLabelToolGUI::OnModelChanged(const QString &modelName) +{ + auto tool = this->GetConnectedToolAs(); + if (nullptr == tool) + { + return; + } + m_Controls.labelListLabel->clear(); + mitk::MonaiModelInfo model = tool->GetModelInfoFromName(modelName.toStdString()); + if (model.IsInteractive()) + { + this->WriteStatusMessage("Interactive model selected. Please press SHIFT + click on the render windows.\n"); + m_Controls.previewButton->setEnabled(false); + this->DisplayWidgets(false); + } + else + { + this->WriteStatusMessage("Auto-segmentation model selected. Please click on Preview.\n"); + m_Controls.previewButton->setEnabled(true); + this->DisplayWidgets(true); + } + auto selectedModel = m_Controls.modelBox->currentText().toStdString(); + for (const auto &modelObject : tool->GetInfoParameters()->models) + { + if (modelObject.name == selectedModel) + { + auto requestObject = std::make_unique(); + requestObject->model = modelObject; + requestObject->hostName = tool->GetInfoParameters()->hostName; + requestObject->port = tool->GetInfoParameters()->port; + if (modelObject.IsInteractive()) // set only if interactive model + { + tool->m_RequestParameters = std::move(requestObject); + } + QStringList supportedLabels; + for (const auto &label : modelObject.labels) + { + supportedLabels << QString::fromStdString(label.first); + } + m_Controls.labelListLabel->setText(supportedLabels.join(QStringLiteral(", "))); + break; + } + } + tool->MonaiStatusEvent += + mitk::MessageDelegate1(this, &QmitkMonaiLabelToolGUI::StatusMessageListener); +} + +void QmitkMonaiLabelToolGUI::OnFetchBtnClicked() +{ + m_Controls.previewButton->setEnabled(false); + m_Controls.labelListLabel->clear(); + auto reply = QMessageBox::question(this, "Confirm", ::CONFIRM_QUESTION_TEXT, QMessageBox::Yes | QMessageBox::No); + if (reply == QMessageBox::No) + { + MITK_INFO << "Didn't went ahead with Monai Label inferencing"; + return; + } + auto tool = this->GetConnectedToolAs(); + if (nullptr == tool) + { + return; + } + QString urlString = m_Controls.urlBox->text(); + QUrl url(urlString); + if (url.isValid() && !url.isLocalFile() && !url.hasFragment() && !url.hasQuery()) // sanity check + { + std::string hostName = url.host().toStdString(); + int port = url.port(); + try + { + tool->FetchOverallInfo(hostName, port); + bool allowAllModels = m_Preferences->GetBool("monailabel allow all models", false); + this->PopulateUI(allowAllModels); + } + catch (const mitk::Exception &e) + { + m_Controls.appBox->clear(); + m_Controls.modelBox->clear(); + MITK_ERROR << e.GetDescription(); + this->WriteErrorMessage(e.GetDescription()); + } + } + else + { + std::string invalidURLMessage = "Invalid URL entered: " + urlString.toStdString(); + MITK_ERROR << invalidURLMessage; + this->ShowErrorMessage(invalidURLMessage); + } +} + +void QmitkMonaiLabelToolGUI::OnPreviewBtnClicked() +{ + auto tool = this->GetConnectedToolAs(); + if (nullptr == tool) + { + return; + } + tool->ClearPicks(); // clear any interactive segmentation from before + auto selectedModel = m_Controls.modelBox->currentText().toStdString(); + for (const auto &modelObject : tool->GetInfoParameters()->models) + { + if (modelObject.name == selectedModel) + { + auto requestObject = std::make_unique(); + requestObject->model = modelObject; + requestObject->hostName = tool->GetInfoParameters()->hostName; + requestObject->port = tool->GetInfoParameters()->port; + tool->m_RequestParameters = std::move(requestObject); + break; + } + } + try + { + tool->UpdatePreview(); + } + catch (const std::exception &e) + { + std::stringstream errorMsg; + errorMsg << "STATUS: Error while processing parameters for MONAI Label segmentation. Reason: " + << e.what(); + this->ShowErrorMessage(errorMsg.str()); + this->WriteErrorMessage(QString::fromStdString(errorMsg.str())); + m_Controls.previewButton->setEnabled(true); + return; + } + catch (...) + { + std::string errorMsg = "Unkown error occured while generation MONAI Label segmentation."; + this->ShowErrorMessage(errorMsg); + m_Controls.previewButton->setEnabled(true); + return; + } +} + +void QmitkMonaiLabelToolGUI::PopulateUI(bool allowAllModels) +{ + auto tool = this->GetConnectedToolAs(); + if (nullptr == tool) + { + return; + } + m_Controls.appBox->clear(); + m_Controls.labelListLabel->clear(); + if (nullptr != tool->GetInfoParameters()) + { + QString appName = QString::fromStdString(tool->GetInfoParameters()->name); + auto autoModels = tool->GetAutoSegmentationModels(m_Dimension); + auto interactiveModels = tool->GetInteractiveSegmentationModels(m_Dimension); + autoModels.insert(autoModels.end(), interactiveModels.begin(), interactiveModels.end()); + this->WriteStatusMessage(appName); + m_Controls.appBox->addItem(appName); + this->PopulateModelBox(appName, autoModels, allowAllModels); + m_Controls.modelBox->setCurrentIndex(-1); + } +} + +void QmitkMonaiLabelToolGUI::PopulateModelBox(QString appName, std::vector models, bool allowAllModels) +{ + m_Controls.modelBox->clear(); + for (const auto &model : models) + { + QString modelName = QString::fromStdString(model.name); + if (allowAllModels) + { + if (::BLACKLISTED_MODELS.contains(modelName) && appName.contains(::BLACKLISTED_MODELS[modelName])) + { + continue; + } + m_Controls.modelBox->addItem(modelName); + } + else + { + if (::WHITELISTED_MODELS.contains(modelName)) + { + m_Controls.modelBox->addItem(modelName); + } + } + } +} + +void QmitkMonaiLabelToolGUI::WriteStatusMessage(const QString &message) +{ + m_Controls.responseNote->setText(message); + m_Controls.responseNote->setStyleSheet("font-weight: bold; color: white"); + qApp->processEvents(); +} + +void QmitkMonaiLabelToolGUI::WriteErrorMessage(const QString &message) +{ + m_Controls.responseNote->setText(message); + m_Controls.responseNote->setStyleSheet("font-weight: bold; color: red"); + qApp->processEvents(); +} + +void QmitkMonaiLabelToolGUI::ShowErrorMessage(const std::string &message) +{ + this->setCursor(Qt::ArrowCursor); + QMessageBox::critical(nullptr, "MONAI Label", message.c_str()); + MITK_WARN << message; +} + +void QmitkMonaiLabelToolGUI::OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent& event) +{ + if (event.GetProperty().rfind("monai", 0) == 0) + { + bool allowAllModels = m_Preferences->GetBool("monailabel allow all models", false); + this->PopulateUI(allowAllModels); + } +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h new file mode 100644 index 0000000000..58739ff2f4 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUI.h @@ -0,0 +1,92 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef QmitkMonaiLabelToolGUI_h +#define QmitkMonaiLabelToolGUI_h + +#include "QmitkMultiLabelSegWithPreviewToolGUIBase.h" +#include "ui_QmitkMonaiLabelToolGUIControls.h" +#include +#include +#include +#include +#include + +class MITKSEGMENTATIONUI_EXPORT QmitkMonaiLabelToolGUI : public QmitkMultiLabelSegWithPreviewToolGUIBase +{ + Q_OBJECT + +public: + mitkClassMacro(QmitkMonaiLabelToolGUI, QmitkMultiLabelSegWithPreviewToolGUIBase); + itkCloneMacro(Self); + +protected slots: + + void OnPreviewBtnClicked(); + void OnFetchBtnClicked(); + void OnModelChanged(const QString &); + +protected: + QmitkMonaiLabelToolGUI(int dimension); + ~QmitkMonaiLabelToolGUI(); + + void ConnectNewTool(mitk::SegWithPreviewTool *newTool) override; + void InitializeUI(QBoxLayout *mainLayout) override; + + void EnableWidgets(bool enabled) override; + + virtual void DisplayWidgets(bool enabled); + + /** + * @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 Creates a QMessage object and shows on screen. + */ + void ShowErrorMessage(const std::string &message); + + /** + * @brief Function to listen to tool class status emitters. + */ + void StatusMessageListener(const bool status); + + /** + * @brief Function to listen to Preference changes + */ + void OnPreferenceChangedEvent(const mitk::IPreferences::ChangeEvent &event); + + /** + * @brief Helper function to write MONAI model info in to model combo box + */ + void PopulateModelBox(QString appName, std::vector models, bool allowAllModels); + + /** + * @brief Helper function to populate required server metadata into UI + */ + void PopulateUI(bool allowAllModels); + +private: + mitk::IPreferences *m_Preferences; + Ui_QmitkMonaiLabelToolGUIControls m_Controls; + bool m_FirstPreviewComputation = true; + EnableConfirmSegBtnFunctionType m_SuperclassEnableConfirmSegBtnFnc; + int m_Dimension; +}; + +#endif diff --git a/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui new file mode 100644 index 0000000000..3344e86c13 --- /dev/null +++ b/Modules/SegmentationUI/Qmitk/QmitkMonaiLabelToolGUIControls.ui @@ -0,0 +1,208 @@ + + + QmitkMonaiLabelToolGUIControls + + + + 0 + 0 + 699 + 352 + + + + + 0 + 0 + + + + + 100 + 0 + + + + + 100000 + 100000 + + + + QmitkMonaiToolWidget + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + 0 + 0 + + + + MONAI Label Server URL: + + + + + + + https://localhost:8000 + + + + + + + + + + + 0 + 0 + + + + Available Apps: + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Welcome to MONAI Label App in MITK. [Experimental]</p><p>Please note that this is only an interface to MONAI Label. MITK does not ship with any apps. Make sure to have a started a MONAI Label server beforehand. </p><p>Refer to <a href="https://docs.monai.io/projects/label/en/latest/"><span style=" text-decoration: underline; color:#0000ff;">https://docs.monai.io/projects/label/en/latest/</span></a> to learn everything about the MONAI Label App.</p><p>Provide a valid URL (eg. https://localhost:8000) to the server &amp; start the workflow.</p><p><br/></p></body></html> + + + Qt::RichText + + + true + + + + + + + + + + + 0 + 0 + + + + Models: + + + + + + + + + + + 0 + 0 + + + + Supported Classes: + + + + + + + + 0 + 0 + + + + Qt::RichText + + + true + + + + + + + + + + 0 + 0 + + + + <html><head/><body><p>Welcome to MONAI Label in MITK. [Experimental]</p></body></html> + + + Qt::RichText + + + true + + + + + + + + 0 + 0 + + + + + 100000 + 16777215 + + + + Preview + + + + + + + + + ctkComboBox + QComboBox +
ctkComboBox.h
+ 1 +
+
+ + +
diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp index 5d8ef26003..94396a8a8c 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp @@ -1,150 +1,163 @@ /*============================================================================ 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 "QmitkMultiLabelSegWithPreviewToolGUIBase.h" #include "mitkSegWithPreviewTool.h" #include #include QmitkMultiLabelSegWithPreviewToolGUIBase::QmitkMultiLabelSegWithPreviewToolGUIBase() : QmitkSegWithPreviewToolGUIBase(false) { auto enableMLSelectedDelegate = [this](bool enabled) { auto tool = this->GetConnectedToolAs(); return nullptr != tool ? (tool->GetLabelTransferScope() == mitk::SegWithPreviewTool::LabelTransferScope::AllLabels || !tool->GetSelectedLabels().empty()) && enabled : false; }; m_EnableConfirmSegBtnFnc = enableMLSelectedDelegate; } void QmitkMultiLabelSegWithPreviewToolGUIBase::InitializeUI(QBoxLayout* mainLayout) { auto radioTransferAll = new QRadioButton("Transfer all labels", this); radioTransferAll->setToolTip("Transfer all preview labels when confirmed."); radioTransferAll->setChecked(true); connect(radioTransferAll, &QAbstractButton::toggled, this, &QmitkMultiLabelSegWithPreviewToolGUIBase::OnRadioTransferAllClicked); mainLayout->addWidget(radioTransferAll); m_RadioTransferAll = radioTransferAll; auto radioTransferSelected = new QRadioButton("Transfer selected labels", this); radioTransferSelected->setToolTip("Transfer the selected preview labels when confirmed."); radioTransferSelected->setChecked(false); mainLayout->addWidget(radioTransferSelected); m_RadioTransferSelected = radioTransferSelected; m_LabelSelectionList = new QmitkSimpleLabelSetListWidget(this); m_LabelSelectionList->setObjectName(QString::fromUtf8("m_LabelSelectionList")); QSizePolicy sizePolicy2(QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); sizePolicy2.setHorizontalStretch(0); sizePolicy2.setVerticalStretch(0); sizePolicy2.setHeightForWidth(m_LabelSelectionList->sizePolicy().hasHeightForWidth()); m_LabelSelectionList->setSizePolicy(sizePolicy2); m_LabelSelectionList->setMaximumSize(QSize(10000000, 10000000)); m_LabelSelectionList->setVisible(false); mainLayout->addWidget(m_LabelSelectionList); connect(m_LabelSelectionList, &QmitkSimpleLabelSetListWidget::SelectedLabelsChanged, this, &QmitkMultiLabelSegWithPreviewToolGUIBase::OnLabelSelectionChanged); this->OnRadioTransferAllClicked(true); Superclass::InitializeUI(mainLayout); } void QmitkMultiLabelSegWithPreviewToolGUIBase::OnLabelSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels) { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { mitk::SegWithPreviewTool::SelectedLabelVectorType labelIDs; for (const auto& label : selectedLabels) { labelIDs.push_back(label->GetValue()); } tool->SetSelectedLabels(labelIDs); this->ActualizePreviewLabelVisibility(); this->EnableWidgets(true); //used to actualize the ConfirmSeg btn via the delegate; } } void QmitkMultiLabelSegWithPreviewToolGUIBase::ActualizePreviewLabelVisibility() { auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { auto preview = tool->GetPreviewSegmentation(); if (nullptr != preview) { auto labels = preview->GetLabelsByValue(preview->GetLabelValuesByGroup(preview->GetActiveLayer())); auto selectedLabels = tool->GetSelectedLabels(); for (auto label : labels) { bool isVisible = tool->GetLabelTransferScope() == mitk::SegWithPreviewTool::LabelTransferScope::AllLabels || (std::find(selectedLabels.begin(), selectedLabels.end(), label->GetValue()) != selectedLabels.end()); label->SetVisible(isVisible); preview->UpdateLookupTable(label->GetValue()); } preview->GetLookupTable()->Modified(); } mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkMultiLabelSegWithPreviewToolGUIBase::OnRadioTransferAllClicked(bool checked) { m_LabelSelectionList->setVisible(!checked); auto tool = this->GetConnectedToolAs(); if (nullptr != tool) { if (checked) { tool->SetLabelTransferScope(mitk::SegWithPreviewTool::LabelTransferScope::AllLabels); } else { tool->SetLabelTransferScope(mitk::SegWithPreviewTool::LabelTransferScope::SelectedLabels); } } this->ActualizePreviewLabelVisibility(); } void QmitkMultiLabelSegWithPreviewToolGUIBase::EnableWidgets(bool enabled) { Superclass::EnableWidgets(enabled); if (nullptr != m_LabelSelectionList) { m_LabelSelectionList->setEnabled(enabled); } if (nullptr != m_RadioTransferAll) { m_RadioTransferAll->setEnabled(enabled); } if (nullptr != m_RadioTransferSelected) { m_RadioTransferSelected->setEnabled(enabled); } } void QmitkMultiLabelSegWithPreviewToolGUIBase::SetLabelSetPreview(const mitk::LabelSetImage* preview) { if (nullptr != m_LabelSelectionList) { m_LabelSelectionList->SetLabelSetImage(preview); } } + +void QmitkMultiLabelSegWithPreviewToolGUIBase::DisplayTransferWidgets(bool enabled) +{ + if (nullptr != m_RadioTransferAll) + { + m_RadioTransferAll->setVisible(enabled); + } + + if (nullptr != m_RadioTransferSelected) + { + m_RadioTransferSelected->setVisible(enabled); + } +} diff --git a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h index 57e419406c..f309330917 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h +++ b/Modules/SegmentationUI/Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h @@ -1,56 +1,62 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef QmitkMultiLabelSegWithPreviewToolGUIBase_h #define QmitkMultiLabelSegWithPreviewToolGUIBase_h #include "QmitkSegWithPreviewToolGUIBase.h" #include "QmitkSimpleLabelSetListWidget.h" #include /** \ingroup org_mitk_gui_qt_interactivesegmentation_internal \brief GUI for tools based on mitk::AutoMLSegmentationWithPreviewTool. This GUI offers an additional list to select the label that should be confirmed. */ class MITKSEGMENTATIONUI_EXPORT QmitkMultiLabelSegWithPreviewToolGUIBase : public QmitkSegWithPreviewToolGUIBase { Q_OBJECT public: mitkClassMacro(QmitkMultiLabelSegWithPreviewToolGUIBase, QmitkSegWithPreviewToolGUIBase); protected slots : void OnLabelSelectionChanged(const QmitkSimpleLabelSetListWidget::LabelVectorType& selectedLabels); void OnRadioTransferAllClicked(bool checked); protected: QmitkMultiLabelSegWithPreviewToolGUIBase(); ~QmitkMultiLabelSegWithPreviewToolGUIBase() = default; void InitializeUI(QBoxLayout* mainLayout) override; void EnableWidgets(bool enabled) override; void SetLabelSetPreview(const mitk::LabelSetImage* preview); void ActualizePreviewLabelVisibility(); + /** + * @brief To toggle visibility of "Transfer all labels" and + * "Transfer selected labels" radio buttons. + */ + void DisplayTransferWidgets(bool enabled); + private: QmitkSimpleLabelSetListWidget* m_LabelSelectionList = nullptr; QWidget* m_RadioTransferAll = nullptr; QWidget* m_RadioTransferSelected = nullptr; }; #endif diff --git a/Modules/SegmentationUI/files.cmake b/Modules/SegmentationUI/files.cmake index c17c063bd9..6b02e44ba8 100644 --- a/Modules/SegmentationUI/files.cmake +++ b/Modules/SegmentationUI/files.cmake @@ -1,126 +1,133 @@ set(CPP_FILES Qmitk/QmitkSegWithPreviewToolGUIBase.cpp Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.cpp Qmitk/QmitkBinaryThresholdToolGUIBase.cpp Qmitk/QmitkBinaryThresholdToolGUI.cpp Qmitk/QmitkBinaryThresholdULToolGUI.cpp Qmitk/QmitkConfirmSegmentationDialog.cpp Qmitk/QmitkCopyToClipBoardDialog.cpp Qmitk/QmitkDrawPaintbrushToolGUI.cpp Qmitk/QmitkErasePaintbrushToolGUI.cpp Qmitk/QmitkEditableContourToolGUIBase.cpp Qmitk/QmitkGrowCutToolGUI.cpp Qmitk/QmitkLiveWireTool2DGUI.cpp Qmitk/QmitkLassoToolGUI.cpp Qmitk/QmitkOtsuTool3DGUI.cpp Qmitk/QmitkPaintbrushToolGUI.cpp Qmitk/QmitkPickingToolGUI.cpp Qmitk/QmitkSlicesInterpolator.cpp Qmitk/QmitkToolGUI.cpp Qmitk/QmitkToolGUIArea.cpp Qmitk/QmitkToolSelectionBox.cpp Qmitk/QmitknnUNetFolderParser.cpp Qmitk/QmitknnUNetToolGUI.cpp Qmitk/QmitknnUNetWorker.cpp Qmitk/QmitknnUNetGPU.cpp Qmitk/QmitkSurfaceStampWidget.cpp Qmitk/QmitkMaskStampWidget.cpp Qmitk/QmitkStaticDynamicSegmentationDialog.cpp Qmitk/QmitkSimpleLabelSetListWidget.cpp Qmitk/QmitkSegmentationTaskListWidget.cpp Qmitk/QmitkTotalSegmentatorToolGUI.cpp Qmitk/QmitkSetupVirtualEnvUtil.cpp Qmitk/QmitkMultiLabelInspector.cpp Qmitk/QmitkMultiLabelManager.cpp Qmitk/QmitkMultiLabelTreeModel.cpp Qmitk/QmitkMultiLabelTreeView.cpp Qmitk/QmitkMultiLabelPresetHelper.cpp Qmitk/QmitkLabelColorItemDelegate.cpp Qmitk/QmitkLabelToggleItemDelegate.cpp Qmitk/QmitkFindSegmentationTaskDialog.cpp Qmitk/QmitkSegmentAnythingToolGUI.cpp Qmitk/QmitkMedSAMToolGUI.cpp + Qmitk/QmitkMonaiLabelToolGUI.cpp + Qmitk/QmitkMonaiLabel2DToolGUI.cpp + Qmitk/QmitkMonaiLabel3DToolGUI.cpp SegmentationUtilities/QmitkBooleanOperationsWidget.cpp SegmentationUtilities/QmitkImageMaskingWidget.cpp SegmentationUtilities/QmitkMorphologicalOperationsWidget.cpp SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidget.cpp SegmentationUtilities/QmitkExtractFromMultiLabelSegmentationWidget.cpp ) set(H_FILES Qmitk/QmitkMultiLabelPresetHelper.h ) set(MOC_H_FILES Qmitk/QmitkSegWithPreviewToolGUIBase.h Qmitk/QmitkMultiLabelSegWithPreviewToolGUIBase.h Qmitk/QmitkBinaryThresholdToolGUIBase.h Qmitk/QmitkBinaryThresholdToolGUI.h Qmitk/QmitkBinaryThresholdULToolGUI.h Qmitk/QmitkConfirmSegmentationDialog.h Qmitk/QmitkCopyToClipBoardDialog.h Qmitk/QmitkDrawPaintbrushToolGUI.h Qmitk/QmitkErasePaintbrushToolGUI.h Qmitk/QmitkEditableContourToolGUIBase.h Qmitk/QmitkGrowCutToolGUI.h Qmitk/QmitkLiveWireTool2DGUI.h Qmitk/QmitkLassoToolGUI.h Qmitk/QmitkOtsuTool3DGUI.h Qmitk/QmitkPaintbrushToolGUI.h Qmitk/QmitkPickingToolGUI.h Qmitk/QmitkSlicesInterpolator.h Qmitk/QmitkToolGUI.h Qmitk/QmitkToolGUIArea.h Qmitk/QmitkToolSelectionBox.h Qmitk/QmitknnUNetFolderParser.h Qmitk/QmitknnUNetToolGUI.h Qmitk/QmitknnUNetGPU.h Qmitk/QmitknnUNetWorker.h Qmitk/QmitknnUNetEnsembleLayout.h Qmitk/QmitkSurfaceStampWidget.h Qmitk/QmitkMaskStampWidget.h Qmitk/QmitkStaticDynamicSegmentationDialog.h Qmitk/QmitkSimpleLabelSetListWidget.h Qmitk/QmitkSegmentationTaskListWidget.h Qmitk/QmitkTotalSegmentatorToolGUI.h Qmitk/QmitkSetupVirtualEnvUtil.h Qmitk/QmitkMultiLabelInspector.h Qmitk/QmitkMultiLabelManager.h Qmitk/QmitkMultiLabelTreeModel.h Qmitk/QmitkMultiLabelTreeView.h Qmitk/QmitkLabelColorItemDelegate.h Qmitk/QmitkLabelToggleItemDelegate.h Qmitk/QmitkFindSegmentationTaskDialog.h Qmitk/QmitkSegmentAnythingToolGUI.h Qmitk/QmitkMedSAMToolGUI.h + Qmitk/QmitkMonaiLabelToolGUI.h + Qmitk/QmitkMonaiLabel2DToolGUI.h + Qmitk/QmitkMonaiLabel3DToolGUI.h SegmentationUtilities/QmitkBooleanOperationsWidget.h SegmentationUtilities/QmitkImageMaskingWidget.h SegmentationUtilities/QmitkMorphologicalOperationsWidget.h SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidget.h SegmentationUtilities/QmitkExtractFromMultiLabelSegmentationWidget.h ) set(UI_FILES Qmitk/QmitkConfirmSegmentationDialog.ui Qmitk/QmitkGrowCutToolWidgetControls.ui Qmitk/QmitkOtsuToolWidgetControls.ui Qmitk/QmitkSurfaceStampWidgetGUIControls.ui Qmitk/QmitkMaskStampWidgetGUIControls.ui Qmitk/QmitknnUNetToolGUIControls.ui Qmitk/QmitkEditableContourToolGUIControls.ui Qmitk/QmitkSegmentationTaskListWidget.ui Qmitk/QmitkTotalSegmentatorGUIControls.ui Qmitk/QmitkMultiLabelInspectorControls.ui Qmitk/QmitkMultiLabelManagerControls.ui Qmitk/QmitkFindSegmentationTaskDialog.ui Qmitk/QmitkSegmentAnythingGUIControls.ui Qmitk/QmitkMedSAMGUIControls.ui + Qmitk/QmitkMonaiLabelToolGUIControls.ui SegmentationUtilities/QmitkBooleanOperationsWidgetControls.ui SegmentationUtilities/QmitkImageMaskingWidgetControls.ui SegmentationUtilities/QmitkMorphologicalOperationsWidgetControls.ui SegmentationUtilities/QmitkConvertToMultiLabelSegmentationWidgetControls.ui SegmentationUtilities/QmitkExtractFromMultiLabelSegmentationWidgetControls.ui ) set(QRC_FILES resources/SegmentationUI.qrc ) diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp index 7ed6b1f9a2..e3f2571e73 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePage.cpp @@ -1,156 +1,159 @@ /*============================================================================ 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 namespace { mitk::IPreferences* GetPreferences() { auto* preferencesService = mitk::CoreServices::GetPreferencesService(); return preferencesService->GetSystemPreferences()->Node("org.mitk.views.segmentation"); } } QmitkSegmentationPreferencePage::QmitkSegmentationPreferencePage() : 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; m_Control = new QWidget(parent); m_Ui->setupUi(m_Control); connect(m_Ui->labelSetPresetToolButton, SIGNAL(clicked()), this, SLOT(OnLabelSetPresetButtonClicked())); connect(m_Ui->suggestionsToolButton, SIGNAL(clicked()), this, SLOT(OnSuggestionsButtonClicked())); this->Update(); m_Initializing = false; } QWidget* QmitkSegmentationPreferencePage::GetQtControl() const { return m_Control; } bool QmitkSegmentationPreferencePage::PerformOk() { auto* prefs = GetPreferences(); prefs->PutBool("compact view", m_Ui->compactViewCheckBox->isChecked()); prefs->PutBool("draw outline", m_Ui->outlineRadioButton->isChecked()); prefs->PutBool("selection mode", m_Ui->selectionModeCheckBox->isChecked()); prefs->Put("label set preset", m_Ui->labelSetPresetLineEdit->text().toStdString()); prefs->PutBool("default label naming", m_Ui->defaultNameRadioButton->isChecked()); prefs->Put("label suggestions", m_Ui->suggestionsLineEdit->text().toStdString()); prefs->PutBool("replace standard suggestions", m_Ui->replaceStandardSuggestionsCheckBox->isChecked()); prefs->PutBool("suggest once", m_Ui->suggestOnceCheckBox->isChecked()); + prefs->PutBool("monailabel allow all models", m_Ui->allowAllModelsCheckBox->isChecked()); return true; } void QmitkSegmentationPreferencePage::PerformCancel() { } void QmitkSegmentationPreferencePage::Update() { auto* prefs = GetPreferences(); m_Ui->compactViewCheckBox->setChecked(prefs->GetBool("compact view", false)); if (prefs->GetBool("draw outline", true)) { m_Ui->outlineRadioButton->setChecked(true); } else { m_Ui->overlayRadioButton->setChecked(true); } m_Ui->selectionModeCheckBox->setChecked(prefs->GetBool("selection mode", false)); auto labelSetPreset = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABELSET_PRESET.toStdString(), ""); bool isOverriddenByCmdLineArg = !labelSetPreset.empty(); if (!isOverriddenByCmdLineArg) labelSetPreset = prefs->Get("label set preset", ""); m_Ui->labelSetPresetLineEdit->setDisabled(isOverriddenByCmdLineArg); m_Ui->labelSetPresetToolButton->setDisabled(isOverriddenByCmdLineArg); m_Ui->labelSetPresetCmdLineArgLabel->setVisible(isOverriddenByCmdLineArg); m_Ui->labelSetPresetLineEdit->setText(QString::fromStdString(labelSetPreset)); if (prefs->GetBool("default label naming", true)) { m_Ui->defaultNameRadioButton->setChecked(true); } else { m_Ui->askForNameRadioButton->setChecked(true); } auto labelSuggestions = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), ""); isOverriddenByCmdLineArg = !labelSuggestions.empty(); if (!isOverriddenByCmdLineArg) labelSuggestions = prefs->Get("label suggestions", ""); m_Ui->defaultNameRadioButton->setDisabled(isOverriddenByCmdLineArg); m_Ui->askForNameRadioButton->setDisabled(isOverriddenByCmdLineArg); m_Ui->suggestionsLineEdit->setDisabled(isOverriddenByCmdLineArg); m_Ui->suggestionsToolButton->setDisabled(isOverriddenByCmdLineArg); m_Ui->suggestionsCmdLineArgLabel->setVisible(isOverriddenByCmdLineArg); m_Ui->suggestionsLineEdit->setText(QString::fromStdString(labelSuggestions)); m_Ui->replaceStandardSuggestionsCheckBox->setChecked(prefs->GetBool("replace standard suggestions", true)); m_Ui->suggestOnceCheckBox->setChecked(prefs->GetBool("suggest once", true)); + + m_Ui->allowAllModelsCheckBox->setChecked(prefs->GetBool("monailabel allow all models", 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); } void QmitkSegmentationPreferencePage::OnSuggestionsButtonClicked() { const auto filename = QFileDialog::getOpenFileName(m_Control, QStringLiteral("Load Label Suggestions"), QString(), QStringLiteral("Label suggestions (*.json)")); if (!filename.isEmpty()) m_Ui->suggestionsLineEdit->setText(filename); } diff --git a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui index 419f143f68..f0ddeafdb7 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui +++ b/Plugins/org.mitk.gui.qt.segmentation/src/QmitkSegmentationPreferencePageControls.ui @@ -1,233 +1,253 @@ QmitkSegmentationPreferencePageControls 0 0 656 779 Form Compact view Hide tool button texts and increase icon size 2D display Draw as outline true displayButtonGroup Draw as transparent overlay displayButtonGroup 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 Default label set preset true ... <html><head/><body><p><span style=" color:#ff0000;">The default label set preset is currently overridden 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 Label creation Assign default name and color true labelCreationButtonGroup Ask for name and color labelCreationButtonGroup Label suggestions true ... <html><head/><body><p><span style=" color:#ff0000;">Suggestions are currently enforced by the </span><span style=" font-family:'Courier New'; color:#ff0000;">Segmentation.labelSuggestions</span><span style=" color:#ff0000;"> command-line argument.</span></p></body></html> Qt::RichText true Replace standard organ suggestions true Suggest once per segmentation true + + + + MONAILabel + + + + + + If checked, all models are visible. + + + Allow all models + + + true + + + + Qt::Vertical 20 40 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 aca51f34ff..2db71f1e9b 100644 --- a/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp +++ b/Plugins/org.mitk.gui.qt.segmentation/src/internal/QmitkSegmentationView.cpp @@ -1,1090 +1,1090 @@ /*============================================================================ 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 #include #include #include // Qmitk #include #include #include #include // us #include #include // Qt #include #include #include // vtk #include #include namespace { QList Get2DWindows(const QList allWindows) { QList all2DWindows; for (auto* window : allWindows) { if (window->GetRenderer()->GetMapperID() == mitk::BaseRenderer::Standard2D) { all2DWindows.append(window); } } return all2DWindows; } } 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) , m_DefaultLabelNaming(true) , m_SelectionChangeIsAlreadyBeingHandled(false) { m_SegmentationPredicate = mitk::GetMultiLabelSegmentationPredicate(); m_ReferencePredicate = mitk::GetSegmentationReferenceImagePredicate(); } QmitkSegmentationView::~QmitkSegmentationView() { m_RenderWindowPart = this->GetRenderWindowPart(); if (nullptr != m_Controls) { // deactivate all tools m_ToolManager->ActivateTool(-1); // removing all observers from working data for (NodeTagMapType::iterator dataIter = m_WorkingDataObserverTags.begin(); dataIter != m_WorkingDataObserverTags.end(); ++dataIter) { (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second); } m_WorkingDataObserverTags.clear(); this->RemoveObserversFromWorkingImage(); // removing all observers from reference data for (NodeTagMapType::iterator dataIter = m_ReferenceDataObserverTags.begin(); dataIter != m_ReferenceDataObserverTags.end(); ++dataIter) { (*dataIter).first->GetProperty("visible")->RemoveObserver((*dataIter).second); } m_ReferenceDataObserverTags.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); } m_ToolManager->ActiveToolChanged -= mitk::MessageDelegate(this, &Self::ActiveToolChanged); delete m_Controls; } /**********************************************************************/ /* private Q_SLOTS */ /**********************************************************************/ void QmitkSegmentationView::OnReferenceSelectionChanged(QList) { this->OnAnySelectionChanged(); } void QmitkSegmentationView::OnSegmentationSelectionChanged(QList) { this->OnAnySelectionChanged(); } void QmitkSegmentationView::OnAnySelectionChanged() { // When only a segmentation has been selected and the method is then called by a reference image selection, // the already selected segmentation may not match the geometry predicate of the new reference image anymore. // This will trigger a recursive call of this method further below. While it would be resolved gracefully, we // can spare the extra call with an early-out. The original call of this method will handle the segmentation // selection change afterwards anyway. if (m_SelectionChangeIsAlreadyBeingHandled) return; auto selectedReferenceNode = m_Controls->referenceNodeSelector->GetSelectedNode(); bool referenceNodeChanged = false; m_ToolManager->ActivateTool(-1); if (m_ReferenceNode != selectedReferenceNode) { referenceNodeChanged = true; // Remove visibility observer for the current reference node if (m_ReferenceDataObserverTags.find(m_ReferenceNode) != m_ReferenceDataObserverTags.end()) { m_ReferenceNode->GetProperty("visible")->RemoveObserver(m_ReferenceDataObserverTags[m_ReferenceNode]); m_ReferenceDataObserverTags.erase(m_ReferenceNode); } // Set new reference node m_ReferenceNode = selectedReferenceNode; m_ToolManager->SetReferenceData(m_ReferenceNode); // Prepare for a potential recursive call when changing node predicates of the working node selector m_SelectionChangeIsAlreadyBeingHandled = true; if (m_ReferenceNode.IsNull()) { // Without a reference image, allow all segmentations to be selected m_Controls->workingNodeSelector->SetNodePredicate(m_SegmentationPredicate); m_SelectionChangeIsAlreadyBeingHandled = false; } else { // With a reference image, only allow segmentations that fit the geometry of the reference image to be selected. m_Controls->workingNodeSelector->SetNodePredicate(mitk::GetMultiLabelSegmentationPredicate(m_ReferenceNode->GetData()->GetGeometry())); m_SelectionChangeIsAlreadyBeingHandled = false; this->ApplySelectionModeOnReferenceNode(); // Add visibility observer for the new reference node auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &Self::ValidateSelectionInput); m_ReferenceDataObserverTags[m_ReferenceNode] = m_ReferenceNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command); } } auto selectedWorkingNode = m_Controls->workingNodeSelector->GetSelectedNode(); bool workingNodeChanged = false; if (m_WorkingNode != selectedWorkingNode) { workingNodeChanged = true; this->RemoveObserversFromWorkingImage(); // Remove visibility observer for the current working node if (m_WorkingDataObserverTags.find(m_WorkingNode) != m_WorkingDataObserverTags.end()) { m_WorkingNode->GetProperty("visible")->RemoveObserver(m_WorkingDataObserverTags[m_WorkingNode]); m_WorkingDataObserverTags.erase(m_WorkingNode); } // Set new working node m_WorkingNode = selectedWorkingNode; m_ToolManager->SetWorkingData(m_WorkingNode); if (m_WorkingNode.IsNotNull()) { this->ApplySelectionModeOnWorkingNode(); // Add visibility observer for the new segmentation node auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &Self::ValidateSelectionInput); m_WorkingDataObserverTags[m_WorkingNode] = m_WorkingNode->GetProperty("visible")->AddObserver(itk::ModifiedEvent(), command); this->AddObserversToWorkingImage(); } } // Reset camera if any selection changed but only if both reference node and working node are set if ((referenceNodeChanged || workingNodeChanged) && (m_ReferenceNode.IsNotNull() && m_WorkingNode.IsNotNull())) { if (nullptr != m_RenderWindowPart) { m_RenderWindowPart->InitializeViews(m_ReferenceNode->GetData()->GetTimeGeometry(), false); } } this->UpdateGUI(); } void QmitkSegmentationView::OnLabelAdded(mitk::LabelSetImage::LabelValueType) { this->ValidateSelectionInput(); } void QmitkSegmentationView::OnLabelRemoved(mitk::LabelSetImage::LabelValueType) { this->ValidateSelectionInput(); } void QmitkSegmentationView::OnGroupRemoved(mitk::LabelSetImage::GroupIndexType) { this->ValidateSelectionInput(); } mitk::LabelSetImage* QmitkSegmentationView::GetWorkingImage() { if (m_WorkingNode.IsNull()) return nullptr; return dynamic_cast(m_WorkingNode->GetData()); } void QmitkSegmentationView::AddObserversToWorkingImage() { auto* workingImage = this->GetWorkingImage(); if (workingImage != nullptr) { auto& widget = *this; m_LabelAddedObserver.Reset(workingImage, mitk::LabelAddedEvent(), [&widget](const itk::EventObject& event) { auto labelEvent = dynamic_cast(&event); widget.OnLabelAdded(labelEvent->GetLabelValue()); }); m_LabelRemovedObserver.Reset(workingImage, mitk::LabelRemovedEvent(), [&widget](const itk::EventObject& event) { auto labelEvent = dynamic_cast(&event); widget.OnLabelRemoved(labelEvent->GetLabelValue()); }); m_GroupRemovedObserver.Reset(workingImage, mitk::GroupRemovedEvent(), [&widget](const itk::EventObject& event) { auto groupEvent = dynamic_cast(&event); widget.OnGroupRemoved(groupEvent->GetGroupID()); }); } } void QmitkSegmentationView::RemoveObserversFromWorkingImage() { m_LabelAddedObserver.Reset(); m_LabelRemovedObserver.Reset(); m_GroupRemovedObserver.Reset(); } 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(); auto labels = workingImage->GetLabelValuesByGroup(workingImage->GetActiveLayer()); auto it = std::find(labels.begin(), labels.end(), workingImage->GetActiveLabel()->GetValue()); if (it != labels.end()) ++it; if (it == labels.end()) { it = labels.begin(); } workingImage->SetActiveLabel(*it); m_Controls->multiLabelWidget->SetSelectedLabel(*it); 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; } auto segTemplateImage = referenceImage; if (referenceImage->GetDimension() > 3) { QmitkStaticDynamicSegmentationDialog dialog(m_Parent); dialog.SetReferenceImage(referenceImage.GetPointer()); dialog.exec(); segTemplateImage = dialog.GetSegmentationTemplate(); } 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; } const auto labelSetPreset = this->GetDefaultLabelSetPreset(); if (labelSetPreset.empty() || !mitk::MultiLabelIOHelper::LoadLabelSetImagePreset(labelSetPreset, newLabelSetImage)) { auto newLabel = mitk::LabelSetImageHelper::CreateNewLabel(newLabelSetImage); if (!m_DefaultLabelNaming) QmitkNewSegmentationDialog::DoRenameLabel(newLabel, nullptr, m_Parent); newLabelSetImage->AddLabel(newLabel, newLabelSetImage->GetActiveLayer()); } 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::OnCurrentLabelSelectionChanged(QmitkMultiLabelManager::LabelValueVectorType labels) { auto segmentation = this->GetCurrentSegmentation(); const auto labelValue = labels.front(); if (nullptr == segmentation->GetActiveLabel() || labelValue != segmentation->GetActiveLabel()->GetValue()) { segmentation->SetActiveLabel(labelValue); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } m_Controls->slicesInterpolator->SetActiveLabelValue(labelValue); } void QmitkSegmentationView::OnGoToLabel(mitk::LabelSetImage::LabelValueType /*label*/, const mitk::Point3D& pos) { if (m_RenderWindowPart) { m_RenderWindowPart->SetSelectedPosition(pos); } } void QmitkSegmentationView::OnLabelRenameRequested(mitk::Label* label, bool rename, bool& canceled) const { auto segmentation = this->GetCurrentSegmentation(); if (rename) { canceled = !QmitkNewSegmentationDialog::DoRenameLabel(label, segmentation, this->m_Parent, QmitkNewSegmentationDialog::Mode::RenameLabel); return; } canceled = !QmitkNewSegmentationDialog::DoRenameLabel(label, nullptr, this->m_Parent, QmitkNewSegmentationDialog::Mode::NewLabel); } mitk::LabelSetImage* QmitkSegmentationView::GetCurrentSegmentation() const { auto workingNode = m_Controls->workingNodeSelector->GetSelectedNode(); if (workingNode.IsNull()) mitkThrow() << "Segmentation view is in an invalid state. Working node is null, but a label selection change has been triggered."; auto segmentation = dynamic_cast(workingNode->GetData()); if (nullptr == segmentation) mitkThrow() << "Segmentation view is in an invalid state. Working node contains no segmentation, but a label selection change has been triggered."; return segmentation; } /**********************************************************************/ /* private */ /**********************************************************************/ void QmitkSegmentationView::CreateQtPartControl(QWidget* parent) { m_Parent = parent; m_Controls = new Ui::QmitkSegmentationViewControls; m_Controls->setupUi(parent); // *------------------------ // * SHORTCUTS // *------------------------ QShortcut* visibilityShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_H), parent); connect(visibilityShortcut, &QShortcut::activated, this, &Self::OnVisibilityShortcutActivated); QShortcut* labelToggleShortcut = new QShortcut(QKeySequence(Qt::CTRL | Qt::Key::Key_L, Qt::CTRL | Qt::Key::Key_N), parent); connect(labelToggleShortcut, &QShortcut::activated, this, &Self::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, &Self::OnReferenceSelectionChanged); connect(m_Controls->workingNodeSelector, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &Self::OnSegmentationSelectionChanged); // *------------------------ // * TOOLMANAGER // *------------------------ m_ToolManager = mitk::ToolManagerProvider::GetInstance()->GetToolManager(); m_ToolManager->SetDataStorage(*(this->GetDataStorage())); m_ToolManager->InitializeTools(); - QString segTools2D = tr("Add Subtract Lasso Fill Erase Close Paint Wipe 'Region Growing' 'Live Wire' 'Segment Anything', 'MedSAM'"); - QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' Picking GrowCut TotalSegmentator"); + QString segTools2D = tr("Add Subtract Lasso Fill Erase Close Paint Wipe 'Region Growing' 'Live Wire' 'Segment Anything' 'MedSAM' 'MONAI Label 2D'"); + QString segTools3D = tr("Threshold 'UL Threshold' Otsu 'Region Growing 3D' Picking GrowCut TotalSegmentator 'MONAI Label 3D'"); #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()); connect(m_Controls->toolSelectionBox2D, &QmitkToolSelectionBox::ToolSelected, this, &Self::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->slicesInterpolator->SetDataStorage(this->GetDataStorage()); // create general signal / slot connections connect(m_Controls->newSegmentationButton, &QToolButton::clicked, this, &Self::OnNewSegmentation); connect(m_Controls->slicesInterpolator, &QmitkSlicesInterpolator::SignalShowMarkerNodes, this, &Self::OnShowMarkerNodes); connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::CurrentSelectionChanged, this, &Self::OnCurrentLabelSelectionChanged); connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::GoToLabel, this, &Self::OnGoToLabel); connect(m_Controls->multiLabelWidget, &QmitkMultiLabelManager::LabelRenameRequested, this, &Self::OnLabelRenameRequested); auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &Self::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::ActiveToolChanged() { if (nullptr == m_RenderWindowPart) { return; } mitk::TimeGeometry* interactionReferenceGeometry = nullptr; auto activeTool = m_ToolManager->GetActiveTool(); if (nullptr != activeTool && m_ReferenceNode.IsNotNull()) { mitk::Image::ConstPointer referenceImage = dynamic_cast(m_ReferenceNode->GetData()); if (referenceImage.IsNotNull()) { // tool activated, reference image available: set reference geometry interactionReferenceGeometry = m_ReferenceNode->GetData()->GetTimeGeometry(); } } // set the interaction reference geometry for the render window part (might be nullptr) m_RenderWindowPart->SetInteractionReferenceGeometry(interactionReferenceGeometry); } 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; } if (nullptr != m_RenderWindowPart) { auto all2DWindows = Get2DWindows(m_RenderWindowPart->GetQmitkRenderWindows().values()); m_Controls->slicesInterpolator->Initialize(m_ToolManager, all2DWindows); if (!m_RenderWindowPart->HasCoupledRenderWindows()) { // react if the active tool changed, only if a render window part with decoupled render windows is used m_ToolManager->ActiveToolChanged += mitk::MessageDelegate(this, &Self::ActiveToolChanged); } } } void QmitkSegmentationView::RenderWindowPartDeactivated(mitk::IRenderWindowPart* /*renderWindowPart*/) { m_RenderWindowPart = nullptr; if (nullptr != m_Parent) { m_Parent->setEnabled(false); } // remove message-connection to make sure no message is processed if no render window part is available m_ToolManager->ActiveToolChanged -= mitk::MessageDelegate(this, &Self::ActiveToolChanged); m_Controls->slicesInterpolator->Uninitialize(); } void QmitkSegmentationView::RenderWindowPartInputChanged(mitk::IRenderWindowPart* /*renderWindowPart*/) { if (nullptr == m_RenderWindowPart) { return; } m_Controls->slicesInterpolator->Uninitialize(); auto all2DWindows = Get2DWindows(m_RenderWindowPart->GetQmitkRenderWindows().values()); m_Controls->slicesInterpolator->Initialize(m_ToolManager, all2DWindows); } void QmitkSegmentationView::OnPreferencesChanged(const mitk::IPreferences* prefs) { auto labelSuggestions = mitk::BaseApplication::instance().config().getString(mitk::BaseApplication::ARG_SEGMENTATION_LABEL_SUGGESTIONS.toStdString(), ""); m_DefaultLabelNaming = labelSuggestions.empty() ? prefs->GetBool("default label naming", true) : false; // No default label naming when label suggestions are enforced via command-line argument if (nullptr != m_Controls) { m_Controls->multiLabelWidget->SetDefaultLabelNaming(m_DefaultLabelNaming); bool compactView = prefs->GetBool("compact view", false); int numberOfColumns = compactView ? 6 : 4; m_Controls->toolSelectionBox2D->SetLayoutColumns(numberOfColumns); m_Controls->toolSelectionBox2D->SetShowNames(!compactView); m_Controls->toolSelectionBox3D->SetLayoutColumns(numberOfColumns); m_Controls->toolSelectionBox3D->SetShowNames(!compactView); } m_DrawOutline = prefs->GetBool("draw outline", true); m_SelectionMode = prefs->GetBool("selection mode", false); m_LabelSetPresetPreference = QString::fromStdString(prefs->Get("label set preset", "")); this->ApplyDisplayOptions(); this->ApplySelectionMode(); } void QmitkSegmentationView::NodeAdded(const mitk::DataNode* node) { if (m_SegmentationPredicate->CheckNode(node)) this->ApplyDisplayOptions(const_cast(node)); this->ApplySelectionMode(); } 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; auto image = dynamic_cast(node->GetData()); mitk::SurfaceInterpolationController::GetInstance()->RemoveInterpolationSession(image); } 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) { return; } // the 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 mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkSegmentationView::ApplySelectionMode() { if (!m_SelectionMode) return; this->ApplySelectionModeOnReferenceNode(); this->ApplySelectionModeOnWorkingNode(); } void QmitkSegmentationView::ApplySelectionModeOnReferenceNode() { this->ApplySelectionMode(m_ReferenceNode, m_ReferencePredicate); } void QmitkSegmentationView::ApplySelectionModeOnWorkingNode() { this->ApplySelectionMode(m_WorkingNode, m_SegmentationPredicate); } void QmitkSegmentationView::ApplySelectionMode(mitk::DataNode* node, mitk::NodePredicateBase* predicate) { if (!m_SelectionMode || node == nullptr || predicate == nullptr) return; auto nodes = this->GetDataStorage()->GetSubset(predicate); for (auto iter = nodes->begin(); iter != nodes->end(); ++iter) (*iter)->SetVisibility(*iter == node); } 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->ValidateSelectionInput(); } void QmitkSegmentationView::ValidateSelectionInput() { auto referenceNode = m_Controls->referenceNodeSelector->GetSelectedNode(); auto workingNode = m_Controls->workingNodeSelector->GetSelectedNode(); bool hasReferenceNode = referenceNode.IsNotNull(); bool hasWorkingNode = workingNode.IsNotNull(); bool hasBothNodes = hasReferenceNode && hasWorkingNode; QString warning; bool toolSelectionBoxesEnabled = hasReferenceNode && hasWorkingNode; unsigned int numberOfLabels = 0; m_Controls->multiLabelWidget->setEnabled(hasWorkingNode); m_Controls->toolSelectionBox2D->setEnabled(hasBothNodes); m_Controls->toolSelectionBox3D->setEnabled(hasBothNodes); m_Controls->slicesInterpolator->setEnabled(false); m_Controls->interpolatorWarningLabel->hide(); if (hasReferenceNode) { if (nullptr != m_RenderWindowPart && m_RenderWindowPart->HasCoupledRenderWindows() && !referenceNode->IsVisible(nullptr)) { warning += tr("The selected reference image is currently not visible!"); toolSelectionBoxesEnabled = false; } } if (hasWorkingNode) { if (nullptr != m_RenderWindowPart && m_RenderWindowPart->HasCoupledRenderWindows() && !workingNode->IsVisible(nullptr)) { warning += (!warning.isEmpty() ? "
" : "") + tr("The selected segmentation is currently not visible!"); toolSelectionBoxesEnabled = false; } m_ToolManager->SetReferenceData(referenceNode); m_ToolManager->SetWorkingData(workingNode); m_Controls->multiLabelWidget->setEnabled(true); m_Controls->toolSelectionBox2D->setEnabled(true); m_Controls->toolSelectionBox3D->setEnabled(true); auto labelSetImage = dynamic_cast(workingNode->GetData()); numberOfLabels = labelSetImage->GetTotalNumberOfLabels(); if (numberOfLabels > 0) m_Controls->slicesInterpolator->setEnabled(true); m_Controls->multiLabelWidget->SetMultiLabelNode(workingNode); if (!m_Controls->multiLabelWidget->GetSelectedLabels().empty()) { m_Controls->slicesInterpolator->SetActiveLabelValue(m_Controls->multiLabelWidget->GetSelectedLabels().front()); } } else { m_Controls->multiLabelWidget->SetMultiLabelNode(nullptr); } toolSelectionBoxesEnabled &= numberOfLabels > 0; // Here we need to check whether the geometry of the selected segmentation image (working image geometry) // is aligned with the geometry of the 3D render window. // It is not allowed to use a geometry different from the working image geometry for segmenting. // We only need to this if the tool selection box would be enabled without this check. // Additionally this check only has to be performed for render window parts with coupled render windows. // For different render window parts the user is given the option to reinitialize each render window individually // (see QmitkRenderWindow::ShowOverlayMessage). if (toolSelectionBoxesEnabled && nullptr != m_RenderWindowPart && m_RenderWindowPart->HasCoupledRenderWindows()) { const mitk::BaseGeometry* workingNodeGeometry = workingNode->GetData()->GetGeometry(); const mitk::BaseGeometry* renderWindowGeometry = m_RenderWindowPart->GetQmitkRenderWindow("3d")->GetSliceNavigationController()->GetCurrentGeometry3D(); if (nullptr != workingNodeGeometry && nullptr != renderWindowGeometry) { if (!mitk::Equal(*workingNodeGeometry->GetBoundingBox(), *renderWindowGeometry->GetBoundingBox(), mitk::eps, true)) { warning += (!warning.isEmpty() ? "
" : "") + tr("Please reinitialize the selected segmentation image!"); toolSelectionBoxesEnabled = false; } } } m_Controls->toolSelectionBox2D->setEnabled(toolSelectionBoxesEnabled); m_Controls->toolSelectionBox3D->setEnabled(toolSelectionBoxesEnabled); this->UpdateWarningLabel(warning); m_ToolManager->SetReferenceData(referenceNode); m_ToolManager->SetWorkingData(workingNode); } void QmitkSegmentationView::UpdateWarningLabel(QString text) { if (text.isEmpty()) { m_Controls->selectionWarningLabel->hide(); } else { m_Controls->selectionWarningLabel->setText("" + text + ""); m_Controls->selectionWarningLabel->show(); } }