diff --git a/CMakeLists.txt b/CMakeLists.txt index 4db599bd2d..75ca0cfe03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1436 +1,1385 @@ #[[ 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) #----------------------------------------------------------------------------- # 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 2022.04.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() -#----------------------------------------------------------------------------- -# Check miminum macOS version -#----------------------------------------------------------------------------- -# The minimum supported macOS version is 10.14. If you use a version less than -# 10.14, there is no guarantee that the build still works. -if(APPLE) - exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE macos_version) - if (macos_version VERSION_LESS "10.14") - message(WARNING "Detected macOS version \"${macos_version}\" is not supported anymore. Minimum required macOS version is at least 10.14.") - endif() - if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.14) - message(WARNING "Detected macOS deployment target \"${CMAKE_OSX_DEPLOYMENT_TARGET}\" is not supported anymore. Minimum required macOS version is at least 10.14.") - endif() -endif() - -#----------------------------------------------------------------------------- -# Check miminum compiler versions -#----------------------------------------------------------------------------- - -if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") - # require at least gcc 4.9 as provided by ppa:ubuntu-toolchain-r/test for Ubuntu 14.04 - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) - message(FATAL_ERROR "GCC version must be at least 4.9 -If you are using Ubuntu 14.04, you can easily install gcc and g++ 4.9 (or any later version available) in addition to your version ${CMAKE_CXX_COMPILER_VERSION}: - sudo add-apt-repository ppa:ubuntu-toolchain-r/test - sudo apt-get update - sudo apt-get install gcc-4.9 g++-4.9 -Make sure to explicitly specify these compilers when configuring MITK: - CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-4.9 - CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-4.9 -For more information on the proposed PPA see the Toolchain Updates section of https://wiki.ubuntu.com/ToolChain.") - endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") - # require at least clang 3.4 - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4) - message(FATAL_ERROR "Clang version must be at least 3.4") - endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") - # require at least clang 5.0 - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) - message(FATAL_ERROR "Apple Clang version must be at least 5.0") - endif() -elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") - # require at least Visual Studio 2017 - if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) - message(FATAL_ERROR "Microsoft Visual Studio 2017 or newer required") - endif() -else() - message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang (Linux or Apple), GCC and MSVC.") -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_OpenCL "Use OpenCL GPU-Computing library" OFF) 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 "Custom" 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_Qt5 "Use Qt 5 library" ON) if(MITK_USE_Qt5) if(WIN32) set(MITK_QT5_MINIMUM_VERSION 5.12.9) else() set(MITK_QT5_MINIMUM_VERSION 5.12) endif() set(MITK_QT5_COMPONENTS Concurrent OpenGL PrintSupport Script Sql Svg Widgets Xml XmlPatterns WebEngineWidgets UiTools Help LinguistTools) if(APPLE) list(APPEND MITK_QT5_COMPONENTS DBus) elseif(UNIX) list(APPEND MITK_QT5_COMPONENTS X11Extras) endif() # Hint at default install locations of Qt if(NOT Qt5_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" "msvc2017") # Binary compatible elseif (CMAKE_MATCH_0 EQUAL 2019) list(APPEND _compilers "msvc2017") # 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() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) foreach(_compiler ${_compilers}) set(_glob_expression "${_dir_candidate}/5.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT5_HINTS ${_hints}) endforeach() endforeach() endif() find_package(Qt5 ${MITK_QT5_MINIMUM_VERSION} COMPONENTS ${MITK_QT5_COMPONENTS} REQUIRED HINTS ${MITK_QT5_HINTS}) endif() # ----------------------------------------- # Custom dependency logic if(WIN32 AND Qt5_DIR) set(_dir_candidate "${Qt5_DIR}/../../../../../Tools/OpenSSL/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) 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(NOT OpenSSL_FOUND) set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\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" " 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 AND CMAKE_FRAMEWORK_PATH AND CMAKE_FRAMEWORK_PATH MATCHES "python3\\.?([0-9]+)") find_package(Python3 3.${CMAKE_MATCH_1} EXACT REQUIRED COMPONENTS Interpreter Development NumPy) else() find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy) endif() 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) # Deprecated include(mitkMacroCreateCTKPlugin) #----------------------------------------------------------------------------- # 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) set(MITK_OPENSSL_SSL_DLL "" CACHE FILEPATH "") set(MITK_OPENSSL_CRYPTO_DLL "" CACHE FILEPATH "") 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) # As of Windows 10 Version 1903 (May 2019 Update), applications can use the UTF-8 code page if(WIN32) option(MITK_UTF8 "Use UTF-8 code page in MITK applications on Windows" ON) mark_as_advanced(MITK_UTF8) endif() 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 "externaly 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 explictly 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_OpenIGTLink) link_directories(${OpenIGTLink_LIBRARY_DIRS}) endif() if(MITK_USE_OpenCL) find_package(OpenCL 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_Qt5) find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) # at least Core required get_target_property(_qmake_exec Qt5::qmake LOCATION) execute_process(COMMAND ${_qmake_exec} -query QT_INSTALL_BINS RESULT_VARIABLE _result OUTPUT_VARIABLE QT_BINARY_DIR ERROR_VARIABLE _error ) string(STRIP "${QT_BINARY_DIR}" QT_BINARY_DIR) if(_result OR NOT EXISTS "${QT_BINARY_DIR}") message(FATAL_ERROR "Could not determine Qt binary directory: ${_result} ${QT_BINARY_DIR} ${_error}") endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt5 qhelpgenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt5 qcollectiongenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant assistant-qt5 assistant5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) mark_as_advanced(QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE ) 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_Qt5) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt5 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") 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) #[[ See T27701 # Initial cache for ProjectTemplate and PluginGenerator tests configure_file( CMake/mitkTestInitialCache.txt.in ${MITK_BINARY_DIR}/mitkTestInitialCache.txt @ONLY )]] # 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) 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}\"") endforeach() if(DOXYGEN_FOUND) 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/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/SupportedPlatforms.md b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/SupportedPlatforms.md index 41c0a30b1b..693917cc85 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/SupportedPlatforms.md +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/SettingUpMITK/SupportedPlatforms.md @@ -1,55 +1,55 @@ Supported Platforms {#SupportedPlatformsPage} =================== MITK is a cross-platform framework that is available for the following platforms: - Windows - Linux - macOS Supported Platforms Details --------------------------- The MITK team provides support for the most frequently used platforms and continuously runs testing procedures to ensure compatibility. Due to the large amount of possible combinations of operating systems and compiler versions, we divide platform support into two test categories: Tier 1 and Tier 2. Although MITK may be built on a broader range of platform-compiler combinations, only a subset of these are actively supported by the MITK development team. In general, only 64-bit builds are supported. Tier 1 Platforms ---------------- All Tier 1 platforms are continuously tested by our unit test suite and other internal testing procedures. Errors or bugs discovered in these platforms are prioritized and corrected as soon as possible. | Platform | Compilers | ----------------------------------- | -------------------------------------------------- | Microsoft Windows 10 | Visual Studio 2019 (latest update) +| Linux Ubuntu 22.04 | Default GCC version | Linux Ubuntu 20.04 | Default GCC version -| Linux Ubuntu 18.04 | Default GCC version Tier 2 Platforms ---------------- Tier 2 platforms may or may not be tested on a regular basis. Some Tier 2 platforms are used by individual members of the MITK development team on a daily basis and some only receive occasional testing. While we strive to support these platforms, MITK users should note that errors may be present in released versions as well as in the current master branch. | Platform | Compilers | ----------------------------------- | -------------------------------------------------- | Microsoft Windows 10 | Visual Studio 2022 (latest update) | Apple macOS 11 "Big Sur" | Default Apple Clang version | Apple macOS 10.15 "Catalina" | Default Apple Clang version All platforms not listed above are not officially supported by the MITK team. However, we will happily accept contributions to improve support for other platforms as long as we have the hardware and capacity for maintenance. CI Build Clients ---------------- To get an overview of currently tested platforms, see the build reports on our CDash site. diff --git a/Modules/Core/CMakeLists.txt b/Modules/Core/CMakeLists.txt index 7ffa8c7796..a4e065c8c4 100644 --- a/Modules/Core/CMakeLists.txt +++ b/Modules/Core/CMakeLists.txt @@ -1,87 +1,74 @@ set(TOOL_CPPS "") # temporary suppress warnings in the following files until image accessors are fully integrated. set_source_files_properties( src/DataManagement/mitkImage.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) set_source_files_properties( src/Controllers/mitkSliceNavigationController.cpp COMPILE_FLAGS -DMITK_NO_DEPRECATED_WARNINGS ) -#if(MSVC) -# set(optional_private_package_depends psapi) -#endif() - -if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.1) - set(optional_public_target_depends stdc++fs) -elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) - set(optional_public_target_depends c++fs) -endif() - mitk_create_module( INCLUDE_DIRS PUBLIC ${MITK_BINARY_DIR} PRIVATE src/Algorithms src/Controllers src/DataManagement src/Interactions src/IO src/Rendering DEPENDS PUBLIC mbilog CppMicroServices PACKAGE_DEPENDS PUBLIC Boost ITK|IOImageBase+SpatialObjects+Statistics #ITK|Statistics+Transform VTK|FiltersTexture+FiltersParallel+ImagingStencil+ImagingMath+InteractionStyle+RenderingOpenGL2+RenderingVolumeOpenGL2+RenderingFreeType+RenderingLabel+InteractionWidgets+IOGeometry+IOXML PRIVATE ITK|IOBioRad+IOBMP+IOBruker+IOCSV+IOGDCM+IOGE+IOGIPL+IOHDF5+IOIPL+IOJPEG+IOJPEG2000+IOLSM+IOMesh+IOMeta+IOMINC+IOMRC+IONIFTI+IONRRD+IOPNG+IOSiemens+IOSpatialObjects+IOStimulate+IOTIFF+IOTransformBase+IOTransformHDF5+IOTransformInsightLegacy+IOTransformMatlab+IOVTK+IOXML nlohmann_json tinyxml2 ${optional_private_package_depends} - TARGET_DEPENDS - PUBLIC - ${optional_public_target_depends} # Do not automatically create CppMicroServices initialization code. # Because the VTK "auto-init" functionality injects file-local static # initialization code in every cpp file which includes a VTK header, # static initialization order becomes an issue again. For the Mitk # core library, we need to ensure that the VTK static initialization stuff # happens before the CppMicroServices initialization, since the latter # might already use VTK code which needs to access VTK object factories. # Hence, CppMicroServices initialization code is placed manually within # the mitkCoreActivator.cpp file. NO_INIT ) if(NOT TARGET ${MODULE_TARGET}) message(SEND_ERROR "Core target ${MODULE_TARGET} does not exist") endif() function(_itk_create_factory_register_manager) # In MITK_ITK_Config.cmake, we do *not* include ITK_USE_FILE, which # prevents multiple registrations/unregistrations of ITK IO factories # during library loading/unloading (of MITK libraries). However, we need # "one" place where the IO factories are registered at # least once. This could be the application executable, but every executable would # need to take care of that itself. Instead, we allow the auto registration in the # Mitk Core library. set(NO_DIRECTORY_SCOPED_ITK_COMPILE_DEFINITION 1) find_package(ITK) include(${ITK_USE_FILE}) if(NOT ITK_NO_IO_FACTORY_REGISTER_MANAGER) # We manually add the define which will be of target scope. MITK # patches ITK_USE_FILE to remove the directory scoped compile # definition since it would be propagated to other targets in the # same directory scope but these targets might want to *not* # use the ITK factory manager stuff. target_compile_definitions(${MODULE_TARGET} PRIVATE ITK_IO_FACTORY_REGISTER_MANAGER) endif() endfunction() _itk_create_factory_register_manager() if(BUILD_TESTING) add_subdirectory(TestingHelper) add_subdirectory(test) endif() diff --git a/Modules/Core/include/mitkFileSystem.h b/Modules/Core/include/mitkFileSystem.h deleted file mode 100644 index b23d69df0a..0000000000 --- a/Modules/Core/include/mitkFileSystem.h +++ /dev/null @@ -1,24 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef mitkFileSystem_h -#define mitkFileSystem_h - -#if __has_include() - #include - namespace fs = std::filesystem; -#elif __has_include() - #include - namespace fs = std::experimental::filesystem; -#endif - -#endif diff --git a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp index ce8653ac5d..231f8d4aed 100644 --- a/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp +++ b/Modules/Multilabel/autoload/IO/mitkMultilabelIOMimeTypes.cpp @@ -1,75 +1,75 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkMultilabelIOMimeTypes.h" #include -#include +#include #include #include mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::MitkSegmentationTaskListMimeType() : CustomMimeType(SEGMENTATIONTASKLIST_MIMETYPE_NAME()) { this->AddExtension("json"); this->SetCategory("MITK Segmentation Task List"); this->SetComment("MITK Segmentation Task List"); } bool mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::AppliesTo(const std::string& path) const { bool result = CustomMimeType::AppliesTo(path); - if (!fs::exists(path)) // T18572 + if (!std::filesystem::exists(path)) // T18572 return result; std::ifstream file(path); if (!file.is_open()) return false; auto json = nlohmann::json::parse(file, nullptr, false); if (json.is_discarded() || !json.is_object()) return false; if ("MITK Segmentation Task List" != json.value("FileFormat", "")) return false; if (1 != json.value("Version", 0)) return false; return true; } mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType* mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType::Clone() const { return new MitkSegmentationTaskListMimeType(*this); } mitk::MitkMultilabelIOMimeTypes::MitkSegmentationTaskListMimeType mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE() { return MitkSegmentationTaskListMimeType(); } std::string mitk::MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE_NAME() { return IOMimeTypes::DEFAULT_BASE_NAME() + ".segmentationtasklist"; } std::vector mitk::MitkMultilabelIOMimeTypes::Get() { std::vector mimeTypes; mimeTypes.push_back(SEGMENTATIONTASKLIST_MIMETYPE().Clone()); return mimeTypes; } diff --git a/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp b/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp index e883c20426..6afa7c5a81 100644 --- a/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkSegmentationTaskListIO.cpp @@ -1,267 +1,267 @@ /*============================================================================ 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 "mitkSegmentationTaskListIO.h" #include "mitkMultilabelIOMimeTypes.h" #include #include -#include +#include #include namespace mitk { void to_json(nlohmann::json& json, const SegmentationTaskList::Task& task) { if (task.HasName()) json["Name"] = task.GetName(); if (task.HasDescription()) json["Description"] = task.GetDescription(); if (task.HasImage()) json["Image"] = task.GetImage().string(); if (task.HasSegmentation()) json["Segmentation"] = task.GetSegmentation().string(); if (task.HasLabelName()) json["LabelName"] = task.GetLabelName(); if (task.HasLabelNameSuggestions()) json["LabelNameSuggestions"] = task.GetLabelNameSuggestions().string(); if (task.HasPreset()) json["Preset"] = task.GetPreset().string(); if (task.HasResult()) json["Result"] = task.GetResult().string(); if (task.HasDynamic()) json["Dynamic"] = task.GetDynamic(); } void from_json(const nlohmann::json& json, SegmentationTaskList::Task& task) { auto iter = json.find("Name"); if (iter != json.end()) task.SetName(json["Name"].get()); iter = json.find("Description"); if (iter != json.end()) task.SetDescription(json["Description"].get()); iter = json.find("Image"); if (iter != json.end()) task.SetImage(json["Image"].get()); iter = json.find("Segmentation"); if (iter != json.end()) task.SetSegmentation(json["Segmentation"].get()); iter = json.find("LabelName"); if (iter != json.end()) task.SetLabelName(json["LabelName"].get()); iter = json.find("LabelNameSuggestions"); if (iter != json.end()) task.SetLabelNameSuggestions(json["LabelNameSuggestions"].get()); iter = json.find("Preset"); if (iter != json.end()) task.SetPreset(json["Preset"].get()); iter = json.find("Result"); if (iter != json.end()) task.SetResult(json["Result"].get()); iter = json.find("Dynamic"); if (iter != json.end()) task.SetDynamic(json["Dynamic"].get()); } } mitk::SegmentationTaskListIO::SegmentationTaskListIO() : AbstractFileIO(SegmentationTaskList::GetStaticNameOfClass(), MitkMultilabelIOMimeTypes::SEGMENTATIONTASKLIST_MIMETYPE(), "MITK Segmentation Task List") { this->RegisterService(); } std::vector mitk::SegmentationTaskListIO::DoRead() { auto* stream = this->GetInputStream(); std::ifstream fileStream; if (nullptr == stream) { auto filename = this->GetInputLocation(); - if (filename.empty() || !fs::exists(filename)) + if (filename.empty() || !std::filesystem::exists(filename)) mitkThrow() << "Invalid or nonexistent filename: \"" << filename << "\"!"; fileStream.open(filename); if (!fileStream.is_open()) mitkThrow() << "Could not open file \"" << filename << "\" for reading!"; stream = &fileStream; } nlohmann::json json; try { json = nlohmann::json::parse(*stream); } catch (const nlohmann::json::exception& e) { mitkThrow() << e.what(); } if (!json.is_object()) mitkThrow() << "Unknown file format (expected JSON object as root)!"; if ("MITK Segmentation Task List" != json.value("FileFormat", "")) mitkThrow() << "Unknown file format (expected \"MITK Segmentation Task List\")!"; if (1 != json.value("Version", 0)) mitkThrow() << "Unknown file format version (expected \"1\")!"; if (!json.contains("Tasks") || !json["Tasks"].is_array()) mitkThrow() << "Tasks array not found!"; auto segmentationTaskList = SegmentationTaskList::New(); if (json.contains("Name")) segmentationTaskList->SetProperty("name", StringProperty::New(json["Name"].get())); try { if (json.contains("Defaults")) { segmentationTaskList->SetDefaults(json["Defaults"].get()); if (segmentationTaskList->GetDefaults().HasResult()) mitkThrow() << "Defaults must not contain \"Result\"!"; } for (const auto& task : json["Tasks"]) { auto i = segmentationTaskList->AddTask(task.get()); if (!segmentationTaskList->HasImage(i)) mitkThrow() << "Task " << i << " must contain \"Image\"!"; - fs::path imagePath(segmentationTaskList->GetImage(i)); + std::filesystem::path imagePath(segmentationTaskList->GetImage(i)); if (imagePath.is_relative()) { auto inputLocation = this->GetInputLocation(); /* If we have access to properties, we are reading from an MITK scene * file. In this case, paths are still relative to the original input * location, which is preserved in the properties. */ const auto* properties = this->GetProperties(); if (properties != nullptr) properties->GetStringProperty("MITK.IO.reader.inputlocation", inputLocation); - imagePath = fs::path(inputLocation).remove_filename() / imagePath; + imagePath = std::filesystem::path(inputLocation).remove_filename() / imagePath; } - if (!fs::exists(imagePath)) + if (!std::filesystem::exists(imagePath)) mitkThrow() << "Referenced image \"" << imagePath << "\" in task " << i << " does not exist!"; if (!segmentationTaskList->HasResult(i)) mitkThrow() << "Task " << i << " must contain \"Result\"!"; } } catch (const nlohmann::json::type_error& e) { mitkThrow() << e.what(); } std::vector result; result.push_back(segmentationTaskList.GetPointer()); return result; } void mitk::SegmentationTaskListIO::Write() { auto segmentationTaskList = dynamic_cast(this->GetInput()); if (nullptr == segmentationTaskList) mitkThrow() << "Invalid input for writing!"; if (segmentationTaskList->GetNumberOfTasks() == 0) mitkThrow() << "No tasks found!"; auto* stream = this->GetOutputStream(); std::ofstream fileStream; if (nullptr == stream) { auto filename = this->GetOutputLocation(); if (filename.empty()) mitkThrow() << "Neither an output stream nor an output filename was specified!"; fileStream.open(filename); if (!fileStream.is_open()) mitkThrow() << "Could not open file \"" << filename << "\" for writing!"; stream = &fileStream; } if (!stream->good()) mitkThrow() << "Stream for writing is not good!"; nlohmann::ordered_json json = { { "FileFormat", "MITK Segmentation Task List" }, { "Version", 1 }, { "Name", segmentationTaskList->GetProperty("name")->GetValueAsString() } }; nlohmann::json defaults = segmentationTaskList->GetDefaults(); if (!defaults.is_null()) json["Defaults"] = defaults; nlohmann::json tasks; for (const auto& task : *segmentationTaskList) tasks.push_back(task); json["Tasks"] = tasks; *stream << std::setw(2) << json << std::endl; } mitk::SegmentationTaskListIO* mitk::SegmentationTaskListIO::IOClone() const { return new SegmentationTaskListIO(*this); } diff --git a/Modules/Multilabel/mitkSegmentationTaskList.cpp b/Modules/Multilabel/mitkSegmentationTaskList.cpp index 3722445949..07a102f943 100644 --- a/Modules/Multilabel/mitkSegmentationTaskList.cpp +++ b/Modules/Multilabel/mitkSegmentationTaskList.cpp @@ -1,196 +1,196 @@ /*============================================================================ 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 "mitkSegmentationTaskList.h" #include #include mitk::SegmentationTaskList::Task::Task() : m_Defaults(nullptr) { } mitk::SegmentationTaskList::Task::~Task() { } void mitk::SegmentationTaskList::Task::SetDefaults(const Task* defaults) { m_Defaults = defaults; } mitk::SegmentationTaskList::SegmentationTaskList() { // A base data cannot be serialized if empty. To be not considered empty its // geometry must consist of at least one time step. However, a segmentation // task would then appear as invisible spacial object in a scene. This can // be prevented by excluding it from the scene's bounding box calculations. this->GetTimeGeometry()->Expand(1); this->SetProperty("includeInBoundingBox", BoolProperty::New(false)); } mitk::SegmentationTaskList::SegmentationTaskList(const Self& other) : BaseData(other) { } mitk::SegmentationTaskList::~SegmentationTaskList() { } size_t mitk::SegmentationTaskList::GetNumberOfTasks() const { return m_Tasks.size(); } size_t mitk::SegmentationTaskList::AddTask(const Task& subtask) { m_Tasks.push_back(subtask); m_Tasks.back().SetDefaults(&m_Defaults); return m_Tasks.size() - 1; } const mitk::SegmentationTaskList::Task* mitk::SegmentationTaskList::GetTask(size_t index) const { return &m_Tasks.at(index); } mitk::SegmentationTaskList::Task* mitk::SegmentationTaskList::GetTask(size_t index) { return &m_Tasks.at(index); } const mitk::SegmentationTaskList::Task& mitk::SegmentationTaskList::GetDefaults() const { return m_Defaults; } void mitk::SegmentationTaskList::SetDefaults(const Task& defaults) { m_Defaults = defaults; for (auto& subtask : m_Tasks) subtask.SetDefaults(&m_Defaults); } bool mitk::SegmentationTaskList::IsDone() const { for (size_t i = 0; i < m_Tasks.size(); ++i) { if (!this->IsDone(i)) return false; } return true; } bool mitk::SegmentationTaskList::IsDone(size_t index) const { - return fs::exists(this->GetAbsolutePath(m_Tasks.at(index).GetResult())); + return std::filesystem::exists(this->GetAbsolutePath(m_Tasks.at(index).GetResult())); } -fs::path mitk::SegmentationTaskList::GetInputLocation() const +std::filesystem::path mitk::SegmentationTaskList::GetInputLocation() const { std::string inputLocation; this->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", inputLocation); return !inputLocation.empty() - ? fs::path(inputLocation)/*.lexically_normal()*/ // See T29246 - : fs::path(); + ? std::filesystem::path(inputLocation).lexically_normal() + : std::filesystem::path(); } -fs::path mitk::SegmentationTaskList::GetBasePath() const +std::filesystem::path mitk::SegmentationTaskList::GetBasePath() const { return this->GetInputLocation().remove_filename(); } -fs::path mitk::SegmentationTaskList::GetAbsolutePath(const fs::path& path) const +std::filesystem::path mitk::SegmentationTaskList::GetAbsolutePath(const std::filesystem::path& path) const { if (path.empty()) return path; - auto normalizedPath = path/*.lexically_normal()*/; // See T29246 + auto normalizedPath = path.lexically_normal(); return !normalizedPath.is_absolute() ? this->GetBasePath() / normalizedPath : normalizedPath; } -fs::path mitk::SegmentationTaskList::GetInterimPath(const fs::path& path) const +std::filesystem::path mitk::SegmentationTaskList::GetInterimPath(const std::filesystem::path& path) const { if (path.empty() || !path.has_filename()) return path; auto interimPath = path; return interimPath.replace_extension(".interim" + path.extension().string()); } void mitk::SegmentationTaskList::SaveTask(size_t index, const BaseData* segmentation, bool saveAsIntermediateResult) { if (segmentation == nullptr) return; auto path = this->GetAbsolutePath(this->GetResult(index)); auto interimPath = this->GetInterimPath(path); - if (fs::exists(path)) + if (std::filesystem::exists(path)) saveAsIntermediateResult = false; IOUtil::Save(segmentation, saveAsIntermediateResult ? interimPath.string() : path.string()); - if (!saveAsIntermediateResult && fs::exists(interimPath)) + if (!saveAsIntermediateResult && std::filesystem::exists(interimPath)) { std::error_code ec; - fs::remove(interimPath, ec); + std::filesystem::remove(interimPath, ec); } } std::vector::const_iterator mitk::SegmentationTaskList::begin() const { return m_Tasks.begin(); } std::vector::const_iterator mitk::SegmentationTaskList::end() const { return m_Tasks.end(); } std::vector::iterator mitk::SegmentationTaskList::begin() { return m_Tasks.begin(); } std::vector::iterator mitk::SegmentationTaskList::end() { return m_Tasks.end(); } void mitk::SegmentationTaskList::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::SegmentationTaskList::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::SegmentationTaskList::VerifyRequestedRegion() { return true; } void mitk::SegmentationTaskList::SetRequestedRegion(const itk::DataObject*) { } diff --git a/Modules/Multilabel/mitkSegmentationTaskList.h b/Modules/Multilabel/mitkSegmentationTaskList.h index d1513ab3f3..f1f739d9f5 100644 --- a/Modules/Multilabel/mitkSegmentationTaskList.h +++ b/Modules/Multilabel/mitkSegmentationTaskList.h @@ -1,111 +1,111 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkSegmentationTaskList_h #define mitkSegmentationTaskList_h #include #include #include -#include +#include #include namespace mitk { /** \brief A list of segmentation tasks. * * See \ref MITKSegmentationTaskListsPage for more information. */ class MITKMULTILABEL_EXPORT SegmentationTaskList : public BaseData { public: class MITKMULTILABEL_EXPORT Task { public: Task(); ~Task(); void SetDefaults(const Task* defaults); mitkSegmentationTaskValueMacro(std::string, Name) mitkSegmentationTaskValueMacro(std::string, Description) - mitkSegmentationTaskValueMacro(fs::path, Image) - mitkSegmentationTaskValueMacro(fs::path, Segmentation) + mitkSegmentationTaskValueMacro(std::filesystem::path, Image) + mitkSegmentationTaskValueMacro(std::filesystem::path, Segmentation) mitkSegmentationTaskValueMacro(std::string, LabelName) - mitkSegmentationTaskValueMacro(fs::path, LabelNameSuggestions) - mitkSegmentationTaskValueMacro(fs::path, Preset) - mitkSegmentationTaskValueMacro(fs::path, Result) + mitkSegmentationTaskValueMacro(std::filesystem::path, LabelNameSuggestions) + mitkSegmentationTaskValueMacro(std::filesystem::path, Preset) + mitkSegmentationTaskValueMacro(std::filesystem::path, Result) mitkSegmentationTaskValueMacro(bool, Dynamic) private: const Task* m_Defaults; }; mitkClassMacro(SegmentationTaskList, BaseData) itkFactorylessNewMacro(Self) itkCloneMacro(Self) mitkSegmentationTaskListValueMacro(std::string, Name) mitkSegmentationTaskListValueMacro(std::string, Description) - mitkSegmentationTaskListValueMacro(fs::path, Image) - mitkSegmentationTaskListValueMacro(fs::path, Segmentation) + mitkSegmentationTaskListValueMacro(std::filesystem::path, Image) + mitkSegmentationTaskListValueMacro(std::filesystem::path, Segmentation) mitkSegmentationTaskListValueMacro(std::string, LabelName) - mitkSegmentationTaskListValueMacro(fs::path, LabelNameSuggestions) - mitkSegmentationTaskListValueMacro(fs::path, Preset) - mitkSegmentationTaskListValueMacro(fs::path, Result) + mitkSegmentationTaskListValueMacro(std::filesystem::path, LabelNameSuggestions) + mitkSegmentationTaskListValueMacro(std::filesystem::path, Preset) + mitkSegmentationTaskListValueMacro(std::filesystem::path, Result) mitkSegmentationTaskListValueMacro(bool, Dynamic) size_t GetNumberOfTasks() const; size_t AddTask(const Task& subtask); const Task* GetTask(size_t index) const; Task* GetTask(size_t index); const Task& GetDefaults() const; void SetDefaults(const Task& defaults); bool IsDone() const; bool IsDone(size_t index) const; - fs::path GetInputLocation() const; - fs::path GetBasePath() const; - fs::path GetAbsolutePath(const fs::path& path) const; - fs::path GetInterimPath(const fs::path& path) const; + std::filesystem::path GetInputLocation() const; + std::filesystem::path GetBasePath() const; + std::filesystem::path GetAbsolutePath(const std::filesystem::path& path) const; + std::filesystem::path GetInterimPath(const std::filesystem::path& path) const; void SaveTask(size_t index, const BaseData* segmentation, bool saveAsIntermediateResult = false); std::vector::const_iterator begin() const; std::vector::const_iterator end() const; std::vector::iterator begin(); std::vector::iterator end(); void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; void SetRequestedRegion(const itk::DataObject*) override; protected: mitkCloneMacro(Self) SegmentationTaskList(); SegmentationTaskList(const Self& other); ~SegmentationTaskList() override; private: Task m_Defaults; std::vector m_Tasks; }; } #endif diff --git a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp index 6144ccae55..c28b3e54ce 100644 --- a/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp +++ b/Modules/SceneSerialization/src/mitkSceneReaderV1.cpp @@ -1,463 +1,463 @@ /*============================================================================ 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 "mitkSceneReaderV1.h" #include "Poco/Path.h" #include "mitkBaseRenderer.h" #include "mitkIOUtil.h" #include "mitkProgressBar.h" #include "mitkPropertyListDeserializer.h" #include "mitkSerializerMacros.h" #include #include #include -#include +#include #include MITK_REGISTER_SERIALIZER(SceneReaderV1) namespace { typedef std::pair> NodesAndParentsPair; bool NodeSortByLayerIsLessThan(const NodesAndParentsPair &left, const NodesAndParentsPair &right) { if (left.first.IsNotNull() && right.first.IsNotNull()) { int leftLayer; int rightLayer; if (left.first->GetIntProperty("layer", leftLayer) && right.first->GetIntProperty("layer", rightLayer)) { return leftLayer < rightLayer; } else { // fall back to name sort return left.first->GetName() < right.first->GetName(); } } // in all other cases, fall back to stupid pointer comparison // this is not reasonable but at least answers the sorting // question clearly return left.first.GetPointer() < right.first.GetPointer(); } // This is a workaround until we are able to save time-related information in an // actual file format of surfaces. void ApplyProportionalTimeGeometryProperties(mitk::BaseData* data) { auto* geometry = dynamic_cast(data->GetTimeGeometry()); if (nullptr == geometry) return; auto properties = data->GetPropertyList(); float value = 0.0f; if (properties->GetFloatProperty("ProportionalTimeGeometry.FirstTimePoint", value)) { if (value == -std::numeric_limits::infinity()) value = std::numeric_limits::lowest(); geometry->SetFirstTimePoint(value); } if (properties->GetFloatProperty("ProportionalTimeGeometry.StepDuration", value)) geometry->SetStepDuration(value); } - mitk::PropertyList::Pointer DeserializeProperties(const tinyxml2::XMLElement *propertiesElement, const fs::path& basePath) + mitk::PropertyList::Pointer DeserializeProperties(const tinyxml2::XMLElement *propertiesElement, const std::filesystem::path& basePath) { if (propertiesElement == nullptr) return nullptr; - fs::path path(propertiesElement->Attribute("file")); + std::filesystem::path path(propertiesElement->Attribute("file")); if (path.empty()) return nullptr; if (!basePath.empty()) path = basePath / path; auto deserializer = mitk::PropertyListDeserializer::New(); deserializer->SetFilename(path.string()); deserializer->Deserialize(); return deserializer->GetOutput(); } } bool mitk::SceneReaderV1::LoadScene(tinyxml2::XMLDocument &document, const std::string &workingDirectory, DataStorage *storage) { assert(storage); bool error(false); // TODO prepare to detect errors (such as cycles) from wrongly written or edited xml files // Get number of elements to initialze progress bar // 1. if there is a element, // - construct a name for the appropriate serializer // - try to instantiate this serializer via itk object factory // - if serializer could be created, use it to read the file into a BaseData object // - if successful, call the new node's SetData(..) // create a node for the tag "data" and test if node was created typedef std::vector DataNodeVector; DataNodeVector DataNodes; unsigned int listSize = 0; for (auto *element = document.FirstChildElement("node"); element != nullptr; element = element->NextSiblingElement("node")) { ++listSize; } ProgressBar::GetInstance()->AddStepsToDo(listSize * 2); // Deserialize base data properties before reading the actual data to be // able to provide them as read-only meta data to the data reader. std::map baseDataPropertyLists; for (auto *nodeElement = document.FirstChildElement("node"); nodeElement != nullptr; nodeElement = nodeElement->NextSiblingElement("node")) { const auto *uid = nodeElement->Attribute("UID"); if (uid == nullptr) continue; auto *dataElement = nodeElement->FirstChildElement("data"); if (dataElement != nullptr) { auto properties = DeserializeProperties(dataElement->FirstChildElement("properties"), workingDirectory); if (properties.IsNotNull()) baseDataPropertyLists[uid] = properties; } } for (auto *element = document.FirstChildElement("node"); element != nullptr; element = element->NextSiblingElement("node")) { mitk::PropertyList* properties = nullptr; const auto *uid = element->Attribute("UID"); if (uid != nullptr) { auto iter = baseDataPropertyLists.find(uid); if (iter != baseDataPropertyLists.end()) properties = iter->second; } const auto *dataElement = element->FirstChildElement("data"); auto dataNode = this->LoadBaseDataFromDataTag(dataElement, properties, workingDirectory, error); if (dataNode.IsNull()) continue; auto* baseData = dataNode->GetData(); if (baseData != nullptr && properties != nullptr) { baseData->SetPropertyList(properties); ApplyProportionalTimeGeometryProperties(baseData); } DataNodes.push_back(dataNode); ProgressBar::GetInstance()->Progress(); } // iterate all nodes // first level nodes should be elements auto nit = DataNodes.begin(); for (auto *element = document.FirstChildElement("node"); element != nullptr || nit != DataNodes.end(); element = element->NextSiblingElement("node"), ++nit) { mitk::DataNode::Pointer node = *nit; // 1. check child nodes const char *uida = element->Attribute("UID"); std::string uid(""); if (uida) { uid = uida; m_NodeForID[uid] = node.GetPointer(); m_IDForNode[node.GetPointer()] = uid; } else { MITK_ERROR << "No UID found for current node. Node will have no parents."; error = true; } // 2. if there are nodes, // - instantiate the appropriate PropertyListDeSerializer // - use them to construct PropertyList objects // - add these properties to the node (if necessary, use renderwindow name) bool success = DecorateNodeWithProperties(node, element, workingDirectory); if (!success) { MITK_ERROR << "Could not load properties for node."; error = true; } // remember node for later adding to DataStorage m_OrderedNodePairs.push_back(std::make_pair(node, std::list())); // 3. if there are elements, remember parent objects for (auto *source = element->FirstChildElement("source"); source != nullptr; source = source->NextSiblingElement("source")) { const char *sourceUID = source->Attribute("UID"); if (sourceUID) { m_OrderedNodePairs.back().second.push_back(std::string(sourceUID)); } } ProgressBar::GetInstance()->Progress(); } // end for all // sort our nodes by their "layer" property // (to be inserted in that order) m_OrderedNodePairs.sort(&NodeSortByLayerIsLessThan); // remove all unknown parent UIDs for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end(); ++nodesIter) { for (auto parentsIter = nodesIter->second.begin(); parentsIter != nodesIter->second.end();) { if (m_NodeForID.find(*parentsIter) == m_NodeForID.end()) { parentsIter = nodesIter->second.erase(parentsIter); MITK_WARN << "Found a DataNode with unknown parents. Will add it to DataStorage without any parent objects."; error = true; } else { ++parentsIter; } } } // repeat the following loop ... // ... for all created nodes unsigned int lastMapSize(0); while (lastMapSize != m_OrderedNodePairs .size()) // this is to prevent infinite loops; each iteration must at least add one node to DataStorage { lastMapSize = m_OrderedNodePairs.size(); // iterate (layer) ordered nodes backwards // we insert the highest layers first for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end(); ++nodesIter) { bool addThisNode(true); // if any parent node is not yet in DataStorage, skip node for now and check later for (auto parentsIter = nodesIter->second.begin(); parentsIter != nodesIter->second.end(); ++parentsIter) { if (!storage->Exists(m_NodeForID[*parentsIter])) { addThisNode = false; break; } } if (addThisNode) { DataStorage::SetOfObjects::Pointer parents = DataStorage::SetOfObjects::New(); for (auto parentsIter = nodesIter->second.begin(); parentsIter != nodesIter->second.end(); ++parentsIter) { parents->push_back(m_NodeForID[*parentsIter]); } // if all parents are found in datastorage (or are unknown), add node to DataStorage storage->Add(nodesIter->first, parents); // remove this node from m_OrderedNodePairs m_OrderedNodePairs.erase(nodesIter); // break this for loop because iterators are probably invalid break; } } } // All nodes that are still in m_OrderedNodePairs at this point are not part of a proper directed graph structure. // We'll add such nodes without any parent information. for (auto nodesIter = m_OrderedNodePairs.begin(); nodesIter != m_OrderedNodePairs.end(); ++nodesIter) { storage->Add(nodesIter->first); MITK_WARN << "Encountered node that is not part of a directed graph structure. Will be added to DataStorage " "without parents."; error = true; } return !error; } mitk::DataNode::Pointer mitk::SceneReaderV1::LoadBaseDataFromDataTag(const tinyxml2::XMLElement *dataElement, const PropertyList *properties, const std::string &workingDirectory, bool &error) { DataNode::Pointer node; if (dataElement) { const char *filename = dataElement->Attribute("file"); if (filename && strlen(filename) != 0) { try { auto baseData = IOUtil::Load(workingDirectory + Poco::Path::separator() + filename, properties); node = DataNode::New(); node->SetData(baseData); } catch (std::exception &e) { MITK_ERROR << "Error during attempt to read '" << filename << "'. Exception says: " << e.what(); error = true; } if (node.IsNull()) { MITK_ERROR << "Error during attempt to read '" << filename << "'. Factory returned nullptr object."; error = true; } } const char* dataUID = dataElement->Attribute("UID"); if (!error && dataUID != nullptr) { UIDManipulator manip(node->GetData()); manip.SetUID(dataUID); } } // in case there was no element we create a new empty node (for appending a propertylist later) if (node.IsNull()) { node = DataNode::New(); } return node; } void mitk::SceneReaderV1::ClearNodePropertyListWithExceptions(DataNode &node, PropertyList &propertyList) { // Basically call propertyList.Clear(), but implement exceptions (see bug 19354) BaseData *data = node.GetData(); PropertyList::Pointer propertiesToKeep = PropertyList::New(); if (dynamic_cast(data)) { /* Older scene files (before changes of bug 17547) could contain a RenderingMode property with value "LevelWindow_Color". Since bug 17547 this value has been removed and replaced by the default value LookupTable_LevelWindow_Color. This new default value does only result in "black-to-white" CT images (or others) if there is a corresponding lookup table. Such a lookup table is provided as a default value by the Image mapper. Since that value was never present in older scene files, we do well in not removing the new default value here. Otherwise the mapper would fall back to another default which is all the colors of the rainbow :-( */ BaseProperty::Pointer lutProperty = propertyList.GetProperty("LookupTable"); propertiesToKeep->SetProperty("LookupTable", lutProperty); /* Older scene files (before changes of T14807) may contain multi-component images without the "Image.Displayed Component" property. As the treatment as multi-component image and the corresponding visualization options hinges on that property we should not delete it, if it was added by the mapper. This is a fix for the issue reported in T19919. */ BaseProperty::Pointer compProperty = propertyList.GetProperty("Image.Displayed Component"); if (compProperty.IsNotNull()) { propertiesToKeep->SetProperty("Image.Displayed Component", compProperty); } } propertyList.Clear(); propertyList.ConcatenatePropertyList(propertiesToKeep); } bool mitk::SceneReaderV1::DecorateNodeWithProperties(DataNode *node, const tinyxml2::XMLElement *nodeElement, const std::string &workingDirectory) { assert(node); assert(nodeElement); bool error(false); for (auto *properties = nodeElement->FirstChildElement("properties"); properties != nullptr; properties = properties->NextSiblingElement("properties")) { const char *propertiesfilea(properties->Attribute("file")); std::string propertiesfile(propertiesfilea ? propertiesfilea : ""); const char *renderwindowa(properties->Attribute("renderwindow")); std::string renderwindow(renderwindowa ? renderwindowa : ""); PropertyList::Pointer propertyList = node->GetPropertyList(renderwindow); // DataNode implementation always returns a propertylist ClearNodePropertyListWithExceptions(*node, *propertyList); // use deserializer to construct new properties PropertyListDeserializer::Pointer deserializer = PropertyListDeserializer::New(); deserializer->SetFilename(workingDirectory + Poco::Path::separator() + propertiesfile); bool success = deserializer->Deserialize(); error |= !success; PropertyList::Pointer readProperties = deserializer->GetOutput(); if (readProperties.IsNotNull()) { propertyList->ConcatenatePropertyList(readProperties, true); // true = replace } else { MITK_ERROR << "Property list reader did not return a property list. This is an implementation error. Please tell " "your developer."; error = true; } } return !error; } diff --git a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.cpp b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.cpp index fcf0290514..17fe83363b 100644 --- a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.cpp +++ b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationFlowControlView.cpp @@ -1,183 +1,181 @@ /*============================================================================ 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 "org_mitk_gui_qt_flow_segmentation_Activator.h" // Blueberry #include #include #include #include #include //MITK #include #include #include #include #include #include #include // Qmitk #include "QmitkSegmentationFlowControlView.h" #include // Qt #include #include -#include - const std::string QmitkSegmentationFlowControlView::VIEW_ID = "org.mitk.views.flow.control"; QmitkSegmentationFlowControlView::QmitkSegmentationFlowControlView() : m_Controls(new Ui::SegmentationFlowControlView), m_Parent(nullptr) { auto notHelperObject = mitk::NodePredicateNot::New( mitk::NodePredicateProperty::New("helper object")); m_SegmentationPredicate = mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), notHelperObject); m_SegmentationTaskListPredicate = mitk::NodePredicateAnd::New( mitk::TNodePredicateDataType::New(), notHelperObject); berry::PlatformUI::GetWorkbench()->AddWorkbenchListener(this); } QmitkSegmentationFlowControlView::~QmitkSegmentationFlowControlView() { berry::PlatformUI::GetWorkbench()->RemoveWorkbenchListener(this); } bool QmitkSegmentationFlowControlView::PreShutdown(berry::IWorkbench*, bool) { return m_Controls->segmentationTaskListWidget != nullptr ? m_Controls->segmentationTaskListWidget->OnPreShutdown() : true; } void QmitkSegmentationFlowControlView::SetFocus() { m_Controls->btnStoreAndAccept->setFocus(); } void QmitkSegmentationFlowControlView::CreateQtPartControl(QWidget* parent) { m_Controls->setupUi(parent); m_Parent = parent; using Self = QmitkSegmentationFlowControlView; connect(m_Controls->btnStore, &QPushButton::clicked, this, &Self::OnStoreButtonClicked); connect(m_Controls->btnStoreAndAccept, &QPushButton::clicked, this, &Self::OnAcceptButtonClicked); connect(m_Controls->segmentationTaskListWidget, &QmitkSegmentationTaskListWidget::ActiveTaskChanged, this, &Self::OnActiveTaskChanged); connect(m_Controls->segmentationTaskListWidget, &QmitkSegmentationTaskListWidget::CurrentTaskChanged, this, &Self::OnCurrentTaskChanged); m_Controls->btnStore->setIcon(berry::QtStyleManager::ThemeIcon(QStringLiteral(":/org_mitk_icons/icons/awesome/scalable/actions/document-save.svg"))); m_Controls->btnStore->setVisible(false); m_Controls->segmentationTaskListWidget->setVisible(false); m_Controls->labelStored->setVisible(false); this->UpdateControls(); m_OutputDir = QString::fromStdString(mitk::BaseApplication::instance().config().getString("flow.outputdir", itksys::SystemTools::GetCurrentWorkingDirectory())); m_OutputDir = QDir::fromNativeSeparators(m_OutputDir); m_FileExtension = QString::fromStdString(mitk::BaseApplication::instance().config().getString("flow.outputextension", "nrrd")); } void QmitkSegmentationFlowControlView::OnStoreButtonClicked() { m_Controls->segmentationTaskListWidget->SaveActiveTask(true); } void QmitkSegmentationFlowControlView::OnAcceptButtonClicked() { if (m_Controls->segmentationTaskListWidget->isVisible()) { m_Controls->segmentationTaskListWidget->SaveActiveTask(); m_Controls->segmentationTaskListWidget->LoadNextUnfinishedTask(); } else { auto nodes = this->GetDataStorage()->GetSubset(m_SegmentationPredicate); for (auto node : *nodes) { QString outputpath = m_OutputDir + "/" + QString::fromStdString(node->GetName()) + "." + m_FileExtension; outputpath = QDir::toNativeSeparators(QDir::cleanPath(outputpath)); mitk::IOUtil::Save(node->GetData(), outputpath.toStdString()); } m_Controls->labelStored->setVisible(true); } } void QmitkSegmentationFlowControlView::OnActiveTaskChanged(const std::optional&) { this->UpdateControls(); } void QmitkSegmentationFlowControlView::OnCurrentTaskChanged(const std::optional&) { this->UpdateControls(); } void QmitkSegmentationFlowControlView::UpdateControls() { auto dataStorage = this->GetDataStorage(); auto hasTaskList = !dataStorage->GetSubset(m_SegmentationTaskListPredicate)->empty(); m_Controls->segmentationTaskListWidget->setVisible(hasTaskList); if (hasTaskList) { auto activeTaskIsShown = m_Controls->segmentationTaskListWidget->ActiveTaskIsShown(); m_Controls->btnStore->setVisible(activeTaskIsShown); m_Controls->btnStoreAndAccept->setEnabled(activeTaskIsShown); } else { auto hasSegmentation = !dataStorage->GetSubset(m_SegmentationPredicate)->empty(); m_Controls->btnStore->setVisible(false); m_Controls->btnStoreAndAccept->setEnabled(hasSegmentation); } } void QmitkSegmentationFlowControlView::NodeAdded(const mitk::DataNode* node) { if (dynamic_cast(node->GetData()) != nullptr) this->UpdateControls(); } void QmitkSegmentationFlowControlView::NodeChanged(const mitk::DataNode* node) { if (dynamic_cast(node->GetData()) != nullptr) this->UpdateControls(); } void QmitkSegmentationFlowControlView::NodeRemoved(const mitk::DataNode* node) { if (dynamic_cast(node->GetData()) != nullptr) this->UpdateControls(); } diff --git a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.cpp b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.cpp index b85e6e8659..4a3eed1028 100644 --- a/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.cpp +++ b/Plugins/org.mitk.gui.qt.flow.segmentation/src/internal/QmitkSegmentationTaskListWidget.cpp @@ -1,787 +1,787 @@ /*============================================================================ 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 "QmitkSegmentationTaskListWidget.h" #include "org_mitk_gui_qt_flow_segmentation_Activator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include -#include +#include namespace { berry::IPreferences::Pointer GetSegmentationPreferences() { return berry::Platform::GetPreferencesService()->GetSystemPreferences()->Node("/org.mitk.views.segmentation"); } mitk::DataStorage* GetDataStorage() { auto* pluginContext = org_mitk_gui_qt_flow_segmentation_Activator::GetContext(); auto dataStorageServiceReference = pluginContext->getServiceReference(); if (dataStorageServiceReference) { auto* dataStorageService = pluginContext->getService(dataStorageServiceReference); if (dataStorageService != nullptr) { auto dataStorageReference = dataStorageService->GetDataStorage(); pluginContext->ungetService(dataStorageServiceReference); return dataStorageReference->GetDataStorage(); } } return nullptr; } - fs::path GetInputLocation(const mitk::BaseData* data) + std::filesystem::path GetInputLocation(const mitk::BaseData* data) { std::string result; if (data != nullptr) data->GetPropertyList()->GetStringProperty("MITK.IO.reader.inputlocation", result); return result; } QString ColorString(const QString& string, const QColor& color, const QColor& backgroundColor = QColor::Invalid) { if (!color.isValid() && !backgroundColor.isValid()) return string; auto result = QStringLiteral("%1").arg(string); return result; } } /* This constructor has three objectives: * 1. Do widget initialization that cannot be done in the .ui file * 2. Connect signals and slots * 3. Explicitly trigger a reset to a valid initial widget state */ QmitkSegmentationTaskListWidget::QmitkSegmentationTaskListWidget(QWidget* parent) : QWidget(parent), m_Ui(new Ui::QmitkSegmentationTaskListWidget), m_FileSystemWatcher(new QFileSystemWatcher(this)), m_UnsavedChanges(false) { m_Ui->setupUi(this); m_Ui->selectionWidget->SetDataStorage(GetDataStorage()); m_Ui->selectionWidget->SetSelectionIsOptional(true); m_Ui->selectionWidget->SetEmptyInfo(QStringLiteral("Select a segmentation task list")); m_Ui->selectionWidget->SetAutoSelectNewNodes(true); m_Ui->selectionWidget->SetNodePredicate(mitk::TNodePredicateDataType::New()); m_Ui->progressBar->setStyleSheet(QString("QProgressBar::chunk { background-color: %1; }").arg(QmitkStyleManager::GetIconAccentColor())); using Self = QmitkSegmentationTaskListWidget; connect(m_Ui->selectionWidget, &QmitkSingleNodeSelectionWidget::CurrentSelectionChanged, this, &Self::OnSelectionChanged); connect(m_Ui->previousButton, &QToolButton::clicked, this, &Self::OnPreviousButtonClicked); connect(m_Ui->nextButton, &QToolButton::clicked, this, &Self::OnNextButtonClicked); connect(m_Ui->loadButton, &QPushButton::clicked, this, &Self::OnLoadButtonClicked); connect(m_FileSystemWatcher, &QFileSystemWatcher::directoryChanged, this, &Self::OnResultDirectoryChanged); this->OnSelectionChanged(m_Ui->selectionWidget->GetSelectedNodes()); } QmitkSegmentationTaskListWidget::~QmitkSegmentationTaskListWidget() { } void QmitkSegmentationTaskListWidget::OnUnsavedChangesSaved() { if (m_UnsavedChanges) { m_UnsavedChanges = false; if (this->ActiveTaskIsShown()) this->UpdateDetailsLabel(); } } /* Make sure that the widget transitions into a valid state whenever the * selection changes. */ void QmitkSegmentationTaskListWidget::OnSelectionChanged(const QmitkSingleNodeSelectionWidget::NodeList& nodes) { this->UnloadTasks(); this->ResetControls(); if (!nodes.empty()) { m_TaskListNode = nodes.front(); auto taskList = dynamic_cast(m_TaskListNode->GetData()); if (taskList != nullptr) { this->OnTaskListChanged(taskList); return; } } this->SetTaskList(nullptr); m_TaskListNode = nullptr; } /* Reset all controls to a default state as a common basis for further * adjustments. */ void QmitkSegmentationTaskListWidget::ResetControls() { m_Ui->progressBar->setEnabled(false); m_Ui->progressBar->setFormat(""); m_Ui->progressBar->setValue(0); m_Ui->progressBar->setMaximum(1); m_Ui->previousButton->setEnabled(false); m_Ui->nextButton->setEnabled(false); this->UpdateLoadButton(); this->UpdateDetailsLabel(); } /* If the segmentation task changed, reset all member variables to expected * default values and reset the file system watcher. */ void QmitkSegmentationTaskListWidget::SetTaskList(mitk::SegmentationTaskList* taskList) { if (m_TaskList != taskList) { m_TaskList = taskList; if (taskList != nullptr) { this->SetCurrentTaskIndex(0); } else { this->SetCurrentTaskIndex(std::nullopt); } this->ResetFileSystemWatcher(); } } void QmitkSegmentationTaskListWidget::ResetFileSystemWatcher() { auto paths = m_FileSystemWatcher->directories(); if (!paths.empty()) m_FileSystemWatcher->removePaths(paths); if (m_TaskList.IsNotNull()) { for (const auto& task : *m_TaskList) { auto resultPath = m_TaskList->GetAbsolutePath(task.GetResult()).remove_filename(); - if (!fs::exists(resultPath)) + if (!std::filesystem::exists(resultPath)) { try { - fs::create_directories(resultPath); + std::filesystem::create_directories(resultPath); } - catch (const fs::filesystem_error& e) + catch (const std::filesystem::filesystem_error& e) { MITK_ERROR << e.what(); } } - if (fs::exists(resultPath)) + if (std::filesystem::exists(resultPath)) m_FileSystemWatcher->addPath(QString::fromStdString(resultPath.string())); } } } void QmitkSegmentationTaskListWidget::OnResultDirectoryChanged(const QString&) { // TODO: If a segmentation was modified ("Unsaved changes"), saved ("Done"), and then the file is deleted, the status should be "Unsaved changes" instead of "Not done". this->UpdateProgressBar(); this->UpdateDetailsLabel(); } void QmitkSegmentationTaskListWidget::UpdateProgressBar() { int progress = 0; for (size_t i = 0; i < m_TaskList->GetNumberOfTasks(); ++i) { if (m_TaskList->IsDone(i)) ++progress; } m_Ui->progressBar->setValue(progress); } /* Provided that a valid segmentation task list is currently selected and the * widget is in its default state, update all controls accordingly. */ void QmitkSegmentationTaskListWidget::OnTaskListChanged(mitk::SegmentationTaskList* taskList) { this->SetTaskList(taskList); const auto numTasks = taskList->GetNumberOfTasks(); m_Ui->progressBar->setMaximum(numTasks); m_Ui->progressBar->setFormat(QStringLiteral("%v/%m Task(s) done")); m_Ui->progressBar->setEnabled(true); this->UpdateProgressBar(); m_Ui->loadButton->setEnabled(true); if (numTasks > 1) m_Ui->nextButton->setEnabled(true); } /* If possible, change the currently displayed task to the previous task. * Enable/disable navigation buttons according to the task's position. */ void QmitkSegmentationTaskListWidget::OnPreviousButtonClicked() { auto current = m_CurrentTaskIndex.value(); if (current != 0) this->SetCurrentTaskIndex(current - 1); this->UpdateNavigationButtons(); } /* If possible, change the currently displayed task to the next task. * Enable/disable navigation buttons according to the task's position. */ void QmitkSegmentationTaskListWidget::OnNextButtonClicked() { const auto maxIndex = m_TaskList->GetNumberOfTasks() - 1; auto current = m_CurrentTaskIndex.value(); if (current < maxIndex) this->SetCurrentTaskIndex(current + 1); this->UpdateNavigationButtons(); } void QmitkSegmentationTaskListWidget::UpdateNavigationButtons() { const auto maxIndex = m_TaskList->GetNumberOfTasks() - 1; const auto current = m_CurrentTaskIndex.value(); m_Ui->previousButton->setEnabled(current != 0); m_Ui->nextButton->setEnabled(current != maxIndex); } /* Update affected controls when the currently displayed task changed. */ void QmitkSegmentationTaskListWidget::OnCurrentTaskChanged() { this->UpdateLoadButton(); this->UpdateNavigationButtons(); this->UpdateDetailsLabel(); } /* Update the load button according to the currently displayed task. */ void QmitkSegmentationTaskListWidget::UpdateLoadButton() { auto text = !this->ActiveTaskIsShown() ? QStringLiteral("Load task") : QStringLiteral("Task"); if (m_CurrentTaskIndex.has_value()) { const auto current = m_CurrentTaskIndex.value(); if (m_TaskList.IsNotNull()) { text += QString(" %1/%2").arg(current + 1).arg(m_TaskList->GetNumberOfTasks()); if (m_TaskList->HasName(current)) text += QStringLiteral(":\n") + QString::fromStdString(m_TaskList->GetName(current)); } m_Ui->loadButton->setDisabled(this->ActiveTaskIsShown()); } else { m_Ui->loadButton->setEnabled(false); } m_Ui->loadButton->setText(text); } /* Update the details label according to the currently display task. * The text is composed of the status of the task and a variable number * of text blocks according to the optional values provided by the task. */ void QmitkSegmentationTaskListWidget::UpdateDetailsLabel() { if (!m_CurrentTaskIndex.has_value()) { m_Ui->detailsLabel->clear(); return; } const auto current = m_CurrentTaskIndex.value(); bool isDone = m_TaskList->IsDone(current); auto details = QString("

Status: %1 / ").arg(this->ActiveTaskIsShown() ? ColorString("Active", Qt::white, QColor(Qt::green).darker()) : ColorString("Inactive", Qt::white, QColor(Qt::red).darker())); if (m_UnsavedChanges && this->ActiveTaskIsShown()) { details += QString("%1

").arg(ColorString("Unsaved changes", Qt::white, QColor(Qt::red).darker())); } else { details += QString("%1

").arg(isDone ? ColorString("Done", Qt::white, QColor(Qt::green).darker()) : ColorString("Not done", Qt::white, QColor(Qt::red).darker())); } if (m_TaskList->HasDescription(current)) details += QString("

Description: %1

").arg(QString::fromStdString(m_TaskList->GetDescription(current))); QStringList stringList; if (m_TaskList->HasImage(current)) stringList << QString::fromStdString("Image: " + m_TaskList->GetImage(current).string()); if (m_TaskList->HasSegmentation(current)) stringList << QString::fromStdString("Segmentation: " + m_TaskList->GetSegmentation(current).string()); if (m_TaskList->HasLabelName(current)) stringList << QString::fromStdString("Label name: " + m_TaskList->GetLabelName(current)); if (m_TaskList->HasLabelNameSuggestions(current)) stringList << QString::fromStdString("Label name suggestions: " + m_TaskList->GetLabelNameSuggestions(current).string()); if (m_TaskList->HasPreset(current)) stringList << QString::fromStdString("Label set preset: " + m_TaskList->GetPreset(current).string()); if (m_TaskList->HasDynamic(current)) stringList << QString("Segmentation type: %1").arg(m_TaskList->GetDynamic(current) ? "Dynamic" : "Static"); if (!stringList.empty()) details += QString("

%1

").arg(stringList.join(QStringLiteral("
"))); m_Ui->detailsLabel->setText(details); } /* Load/activate the currently displayed task. Unload all data nodes from * previously active tasks first, but spare and reuse the image if possible. */ void QmitkSegmentationTaskListWidget::OnLoadButtonClicked() { if (!this->HandleUnsavedChanges() || m_UnsavedChanges) return; m_Ui->loadButton->setEnabled(false); QApplication::setOverrideCursor(Qt::BusyCursor); this->LoadTask(this->GetImageDataNode(m_CurrentTaskIndex.value())); QApplication::restoreOverrideCursor(); } /* If present, return the image data node for the task with the specified * index. Otherwise, return nullptr. */ mitk::DataNode* QmitkSegmentationTaskListWidget::GetImageDataNode(size_t index) const { const auto imagePath = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(index)); auto imageNodes = GetDataStorage()->GetDerivations(m_TaskListNode, mitk::NodePredicateFunction::New([imagePath](const mitk::DataNode* node) { return imagePath == GetInputLocation(node->GetData()); })); return !imageNodes->empty() ? imageNodes->front() : nullptr; } /* If present, return the segmentation data node for the task with the * specified index. Otherwise, return nullptr. */ mitk::DataNode* QmitkSegmentationTaskListWidget::GetSegmentationDataNode(size_t index) const { const auto* imageNode = this->GetImageDataNode(index); if (imageNode != nullptr) { auto segmentations = GetDataStorage()->GetDerivations(imageNode, mitk::TNodePredicateDataType::New()); if (!segmentations->empty()) return segmentations->front(); } return nullptr; } /* Unload all task data nodes but spare the passed image data node. */ void QmitkSegmentationTaskListWidget::UnloadTasks(const mitk::DataNode* skip) { this->UnsubscribeFromActiveSegmentation(); if (m_TaskListNode.IsNotNull()) { mitk::DataStorage::Pointer dataStorage = GetDataStorage(); auto imageNodes = dataStorage->GetDerivations(m_TaskListNode, mitk::TNodePredicateDataType::New()); for (auto imageNode : *imageNodes) { dataStorage->Remove(dataStorage->GetDerivations(imageNode, nullptr, false)); if (imageNode != skip) dataStorage->Remove(imageNode); } } this->SetActiveTaskIndex(std::nullopt); } void QmitkSegmentationTaskListWidget::LoadNextUnfinishedTask() { const auto current = m_CurrentTaskIndex.value(); const auto numTasks = m_TaskList->GetNumberOfTasks(); for (size_t unboundNext = current; unboundNext < current + numTasks; ++unboundNext) { auto next = unboundNext % numTasks; if (!m_TaskList->IsDone(next)) { this->SetCurrentTaskIndex(next); this->OnLoadButtonClicked(); break; } } } /* Load/activate the currently displayed task. The task must specify * an image. The segmentation is either created from scratch with an optional * name for the first label, possibly based on a label set preset specified by * the task, or loaded as specified by the task. If a result file does * exist, it is chosen as segmentation instead. */ void QmitkSegmentationTaskListWidget::LoadTask(mitk::DataNode::Pointer imageNode) { this->UnloadTasks(imageNode); const auto current = m_CurrentTaskIndex.value(); mitk::Image::Pointer image; mitk::LabelSetImage::Pointer segmentation; try { if (imageNode.IsNull()) { const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetImage(current)); image = mitk::IOUtil::Load(path.string()); } const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetResult(current)); const auto interimPath = m_TaskList->GetInterimPath(path); - if (fs::exists(path)) + if (std::filesystem::exists(path)) { segmentation = mitk::IOUtil::Load(path.string()); } - else if (fs::exists(interimPath)) + else if (std::filesystem::exists(interimPath)) { segmentation = mitk::IOUtil::Load(interimPath.string()); } else if (m_TaskList->HasSegmentation(current)) { const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetSegmentation(current)); segmentation = mitk::IOUtil::Load(path.string()); } } catch (const mitk::Exception&) { return; } auto dataStorage = GetDataStorage(); if (imageNode.IsNull()) { imageNode = mitk::DataNode::New(); imageNode->SetData(image); dataStorage->Add(imageNode, m_TaskListNode); mitk::RenderingManager::GetInstance()->InitializeViews(image->GetTimeGeometry()); } else { image = static_cast(imageNode->GetData()); } auto name = "Task " + std::to_string(current + 1); imageNode->SetName(name); if (segmentation.IsNull()) { mitk::Image::ConstPointer templateImage = image; if (templateImage->GetDimension() > 3) { if (m_TaskList->HasDynamic(current)) { if (!m_TaskList->GetDynamic(current)) templateImage = mitk::SegmentationHelper::GetStaticSegmentationTemplate(image); } else { QmitkStaticDynamicSegmentationDialog dialog(this); dialog.SetReferenceImage(templateImage); dialog.exec(); templateImage = dialog.GetSegmentationTemplate(); } } auto segmentationNode = mitk::LabelSetImageHelper::CreateNewSegmentationNode(imageNode, templateImage, name); segmentation = static_cast(segmentationNode->GetData()); if (m_TaskList->HasPreset(current)) { const auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetPreset(current)); mitk::LabelSetIOHelper::LoadLabelSetImagePreset(path.string(), segmentation); } else { auto label = mitk::LabelSetImageHelper::CreateNewLabel(segmentation); if (m_TaskList->HasLabelName(current)) label->SetName(m_TaskList->GetLabelName(current)); segmentation->GetActiveLabelSet()->AddLabel(label); } dataStorage->Add(segmentationNode, imageNode); } else { auto segmentationNode = mitk::DataNode::New(); segmentationNode->SetName(name); segmentationNode->SetData(segmentation); dataStorage->Add(segmentationNode, imageNode); } auto prefs = GetSegmentationPreferences(); if (prefs.IsNotNull()) { if (m_TaskList->HasLabelNameSuggestions(current)) { auto path = m_TaskList->GetAbsolutePath(m_TaskList->GetLabelNameSuggestions(current)); prefs->PutBool("default label naming", false); prefs->Put("label suggestions", QString::fromStdString(path.string())); prefs->PutBool("replace standard suggestions", true); prefs->PutBool("suggest once", true); } else { prefs->PutBool("default label naming", true); prefs->Put("label suggestions", ""); } } m_UnsavedChanges = false; this->SetActiveTaskIndex(current); this->SubscribeToActiveSegmentation(); this->OnCurrentTaskChanged(); } void QmitkSegmentationTaskListWidget::SubscribeToActiveSegmentation() { if (m_ActiveTaskIndex.has_value()) { auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value()); if (segmentationNode != nullptr) { auto segmentation = static_cast(segmentationNode->GetData()); auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkSegmentationTaskListWidget::OnSegmentationModified); m_SegmentationModifiedObserverTag = segmentation->AddObserver(itk::ModifiedEvent(), command); } } } void QmitkSegmentationTaskListWidget::UnsubscribeFromActiveSegmentation() { if (m_ActiveTaskIndex.has_value() && m_SegmentationModifiedObserverTag.has_value()) { auto segmentationNode = this->GetSegmentationDataNode(m_ActiveTaskIndex.value()); if (segmentationNode != nullptr) { auto segmentation = static_cast(segmentationNode->GetData()); segmentation->RemoveObserver(m_SegmentationModifiedObserverTag.value()); } m_SegmentationModifiedObserverTag.reset(); } } void QmitkSegmentationTaskListWidget::OnSegmentationModified() { if (!m_UnsavedChanges) { m_UnsavedChanges = true; if (m_ActiveTaskIndex.value() == m_CurrentTaskIndex) this->UpdateDetailsLabel(); } } void QmitkSegmentationTaskListWidget::SetActiveTaskIndex(const std::optional& index) { if (m_ActiveTaskIndex != index) { m_ActiveTaskIndex = index; emit ActiveTaskChanged(m_ActiveTaskIndex); } } void QmitkSegmentationTaskListWidget::SetCurrentTaskIndex(const std::optional& index) { if (m_CurrentTaskIndex != index) { m_CurrentTaskIndex = index; this->OnCurrentTaskChanged(); emit CurrentTaskChanged(m_CurrentTaskIndex); } } bool QmitkSegmentationTaskListWidget::ActiveTaskIsShown() const { return m_ActiveTaskIndex.has_value() && m_CurrentTaskIndex.has_value() && m_ActiveTaskIndex == m_CurrentTaskIndex; } bool QmitkSegmentationTaskListWidget::HandleUnsavedChanges(const QString& alternativeTitle) { if (m_UnsavedChanges) { const auto active = m_ActiveTaskIndex.value(); const auto current = m_CurrentTaskIndex.value(); QString title; if (alternativeTitle.isEmpty()) { title = QString("Load task %1").arg(current + 1); if (m_TaskList->HasName(current)) title += ": " + QString::fromStdString(m_TaskList->GetName(current)); } else { title = alternativeTitle; } auto text = QString("The currently active task %1 ").arg(active + 1); if (m_TaskList->HasName(active)) text += "(" + QString::fromStdString(m_TaskList->GetName(active)) + ") "; text += "has unsaved changes."; auto reply = QMessageBox::question(this, title, text, QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel, QMessageBox::Cancel); switch (reply) { case QMessageBox::Save: - this->SaveActiveTask(!fs::exists(m_TaskList->GetResult(active))); + this->SaveActiveTask(!std::filesystem::exists(m_TaskList->GetResult(active))); break; case QMessageBox::Discard: m_UnsavedChanges = false; break; default: return false; } } return true; } void QmitkSegmentationTaskListWidget::SaveActiveTask(bool saveAsIntermediateResult) { if (!m_ActiveTaskIndex.has_value()) return; QApplication::setOverrideCursor(Qt::BusyCursor); try { const auto active = m_ActiveTaskIndex.value(); m_TaskList->SaveTask(active, this->GetSegmentationDataNode(active)->GetData(), saveAsIntermediateResult); this->OnUnsavedChangesSaved(); } catch (const mitk::Exception& e) { MITK_ERROR << e; } QApplication::restoreOverrideCursor(); } bool QmitkSegmentationTaskListWidget::OnPreShutdown() { return this->HandleUnsavedChanges(QStringLiteral("Application shutdown")); } diff --git a/README.md b/README.md index 7285997530..f2c8c8469b 100644 --- a/README.md +++ b/README.md @@ -1,110 +1,110 @@ ![MITK Logo][logo] The [Medical Imaging Interaction Toolkit][mitk] (MITK) is a free open-source software system for development of interactive medical image processing software. MITK combines the [Insight Toolkit][itk] (ITK) and the [Visualization Toolkit][vtk] (VTK) with an application framework. The links below provide high-level and reference documentation targeting different usage scenarios: - Get a [high-level overview][mitk-overview] about MITK with pointers to further documentation - End-users looking for help with MITK applications should read the [MITK User Manual][mitk-usermanual] - Developers contributing to or using MITK, please see the [MITK Developer Manual][mitk-devmanual] as well as the [MITK API Reference][mitk-apiref] See the [MITK homepage][mitk] for details. Supported platforms ------------------- MITK is a cross-platform C++ toolkit and officially supports: - Windows - Linux - macOS For details, please read the [Supported Platforms][platforms] page. ### Build status of develop branch [![Windows][windows-build-status]][cdash] -[![Ubuntu 18.04][ubuntu-18.04-build-status]][cdash] [![Ubuntu 20.04][ubuntu-20.04-build-status]][cdash] +[![Ubuntu 22.04][ubuntu-22.04-build-status]][cdash] [![macOS 10.15 Catalina][macos-10.15-build-status]][cdash] [![macOS 11 Big Sur][macos-11-build-status]][cdash] We highly recommend to use the stable **master** branch instead. It is updated 1-2 times per month accompanied by curated [changelogs][changelog] and [snapshot installers][snapshot-installers]. License ------- Copyright (c) [German Cancer Research Center (DKFZ)][dkfz]. All rights reserved. MITK is available as free open-source software under a [3-clause BSD license][license]. Download -------- The MITK source code and binaries for the *MitkWorkbench* application are released regularly according to the [MITK release cycle][release-cycle]. See the [Download][download] page for a list of releases. The official MITK source code is available in the [MITK Git repository][phab_repo]. The Git clone command is git clone https://phabricator.mitk.org/source/mitk.git MITK Active development takes place in the MITK develop branch and its usage is advised for advanced users only. How to contribute ----------------- Contributions of all kind are happily accepted. However, to make the contribution process as smooth as possible, please read the [How to contribute to MITK][contribute] page if you plan to contribute to MITK. Build instructions ------------------ MITK uses [CMake][cmake] to configure a build tree. The following is a crash course about cloning, configuring, and building MITK on a Linux/Unix system: git clone https://phabricator.mitk.org/source/mitk.git MITK mkdir MITK-build cd MITK-build cmake ../MITK make -j4 Read the comprehensive [build instructions][build] page for details. Useful links ------------ - [Homepage][mitk] - [Download][download] - [Mailing list][mailinglist] - [Issue tracker][bugs] [logo]: https://github.com/MITK/MITK/raw/master/mitk.png [mitk]: https://www.mitk.org [itk]: https://itk.org [vtk]: https://vtk.org [mitk-overview]: https://docs.mitk.org/nightly/ [mitk-usermanual]: https://docs.mitk.org/nightly/UserManualPortal.html [mitk-devmanual]: https://docs.mitk.org/nightly/DeveloperManualPortal.html [mitk-apiref]: https://docs.mitk.org/nightly/usergroup0.html [platforms]: https://docs.mitk.org/nightly/SupportedPlatformsPage.html [dkfz]: https://www.dkfz.de [license]: https://github.com/MITK/MITK/blob/master/LICENSE [release-cycle]: https://www.mitk.org/MitkReleaseCycle [download]: https://www.mitk.org/Download [phab_repo]: https://phabricator.mitk.org/source/mitk/ [contribute]: https://www.mitk.org/How_to_contribute [cmake]: https://www.cmake.org [build]: https://docs.mitk.org/nightly/BuildInstructionsPage.html [mailinglist]: https://www.mitk.org/Mailinglist [bugs]: https://phabricator.mitk.org/maniphest/ [cdash]: https://cdash.mitk.org/index.php?project=MITK [changelog]: https://phabricator.mitk.org/w/mitk/changelog/ [snapshot-installers]: https://www.mitk.org/download/ci/snapshots/ [windows-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FWindows&subject=Windows -[ubuntu-18.04-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FUbuntu+18.04&subject=Ubuntu+18.04 +[ubuntu-22.04-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FUbuntu+22.04&subject=Ubuntu+22.04 [ubuntu-20.04-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FUbuntu+20.04&subject=Ubuntu+20.04 [macOS-10.15-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FmacOS+Catalina&subject=macOS+10.15+Catalina [macOS-11-build-status]: https://ci.mitk.org/buildStatus/icon?job=MITK%2FContinuous%2FmacOS+Big+Sur&subject=macOS+11+Big+Sur