diff --git a/.arcconfig b/.arcconfig index 1b723c4095..6b0552a974 100644 --- a/.arcconfig +++ b/.arcconfig @@ -1,5 +1,8 @@ { - "base": "git:merge-base(origin/master)", + "arc.feature.start.default": "develop", + "arc.land.onto.default": "develop", + "base": "git:merge-base(origin/develop)", "history.immutable": true, - "phabricator.uri": "https://phabricator.mitk.org/" + "phabricator.uri": "https://phabricator.mitk.org/", + "repository.callsign": "MITK" } diff --git a/CMake/MITKDashboardScript.TEMPLATE.cmake b/CMake/MITKDashboardScript.TEMPLATE.cmake index 663f15eb44..0d459014e1 100644 --- a/CMake/MITKDashboardScript.TEMPLATE.cmake +++ b/CMake/MITKDashboardScript.TEMPLATE.cmake @@ -1,146 +1,143 @@ # # OS: Ubuntu 9.04 2.6.28-18-generic # Hardware: x86_64 GNU/Linux # GPU: NA # # Note: The specific version and processor type of this machine should be reported in the # header above. Indeed, this file will be send to the dashboard as a NOTE file. cmake_minimum_required(VERSION 3.14.5 FATAL_ERROR) # # Dashboard properties # set(MY_COMPILER "gcc-4.9.x") # For Windows, e.g. #set(MY_COMPILER "VC12.0") set(CTEST_CMAKE_COMMAND "/usr/bin/cmake") set(CTEST_CMAKE_GENERATOR "Unix Makefiles") set(CTEST_DASHBOARD_ROOT "/opt/dartclients") # For Windows, e.g. #set(CTEST_CMAKE_COMMAND "cmake") #set(CTEST_CMAKE_GENERATOR "Visual Studio 16 2019") #set(CTEST_CMAKE_GENERATOR_PLATFORM "x64") #set(CTEST_DASHBOARD_ROOT "D:/dc") set(MITK_EXTENSIONS # "||" # "https://phabricator.mitk.org/source/mitk-projecttemplate.git|master|MITK-ProjectTemplate" # "https://phabricator.mitk.org/source/mitk-diffusion.git|master|MITK-Diffusion" ) # # Dashboard options # set(WITH_KWSTYLE FALSE) set(WITH_MEMCHECK FALSE) set(WITH_COVERAGE FALSE) set(WITH_DOCUMENTATION FALSE) #set(DOCUMENTATION_ARCHIVES_OUTPUT_DIRECTORY ) # for example: $ENV{HOME}/Projects/Doxygen set(CTEST_BUILD_CONFIGURATION "Release") set(CTEST_TEST_TIMEOUT 500) if(UNIX) set(CTEST_BUILD_FLAGS "-j4") # Use multiple CPU cores to build else() set(CTEST_BUILD_FLAGS "") endif() # experimental: # - run_ctest() macro will be called *ONE* time # - binary directory will *NOT* be cleaned # continuous: # - run_ctest() macro will be called EVERY 5 minutes ... # - binary directory will *NOT* be cleaned # - configure/build will be executed *ONLY* if the repository has been updated # nightly: # - run_ctest() macro will be called *ONE* time # - binary directory *WILL BE* cleaned set(SCRIPT_MODE "experimental") # "experimental", "continuous", "nightly" # # Project specific properties # # In order to shorten the global path length, the build directory for each DartClient # uses the following abrevation sceme: # For build configuration: # Debug -> d # Release -> r # For scripte mode: # continuous -> c # nightly -> n # experimental -> e # Example directory: /MITK-sb-d-n/ for a nightly MITK superbuild in debug mode. set(short_of_ctest_build_configuration "") set(short_of_script_mode "") string(SUBSTRING ${CTEST_BUILD_CONFIGURATION} 0 1 short_of_ctest_build_configuration) string(SUBSTRING ${SCRIPT_MODE} 0 1 short_of_script_mode) set(CTEST_SOURCE_DIRECTORY "${CTEST_DASHBOARD_ROOT}/MITK") set(CTEST_BINARY_DIRECTORY "${CTEST_DASHBOARD_ROOT}/MITK-sb-${short_of_ctest_build_configuration}-${short_of_script_mode}") # Create an initial cache file for MITK. This file is used to configure the MITK-Build. Use ADDITIONAL_CMAKECACHE_OPTION # to configure the MITK-Superbuild. The set(MITK_INITIAL_CACHE " # Example how to set a boolean variable in the MITK-Build via this script: #set(MITK_ENABLE_TOF_HARDWARE \"TRUE\" CACHE INTERNAL \"Enable ToF Hardware\") # Example how to set a path variable in the MITK-Build via this script: #set(MITK_PMD_LIB \"/home/kilgus/thomas/PMDSDK2/Linux_x86_64/bin/libpmdaccess2.so\" CACHE INTERNAL \"PMD lib\") ") set(ADDITIONAL_CMAKECACHE_OPTION " # Superbuild variables are not passed through to the MITK-Build (or any other build like ITK, VTK, ...) # Use the MITK_INITIAL_CACHE the pass variables to the MITK-Build. # add entries like this #MITK_USE_OpenCV:BOOL=OFF CMAKE_PREFIX_PATH:PATH=${CMAKE_PREFIX_PATH} ") # List of test that should be explicitly disabled on this machine set(TEST_TO_EXCLUDE_REGEX "") # set any extra environment variables here set(ENV{PATH} "$ENV{PATH}") find_program(CTEST_COVERAGE_COMMAND NAMES gcov) find_program(CTEST_MEMORYCHECK_COMMAND NAMES valgrind) find_program(CTEST_GIT_COMMAND NAMES git) # # Git repository - Overwrite the default value provided by the driver script # # The git repository containing MITK code #set(GIT_REPOSITORY "/home/username/MITK") # The branch of the MITK git repository to check out -#set(GIT_BRANCH "bug-xxx-label") +#set(GIT_BRANCH "develop") ########################################## # WARNING: DO NOT EDIT BEYOND THIS POINT # ########################################## # # Convenient macro allowing to download a file # macro(downloadFile url dest) file(DOWNLOAD "${url}" "${dest}" STATUS status) list(GET status 0 error_code) list(GET status 1 error_msg) if(error_code) message(FATAL_ERROR "error: Failed to download ${url} - ${error_msg}") endif() endmacro() +if(NOT GIT_BRANCH) + set(GIT_BRANCH "master") +endif() + # # Download and include setup script # -if(NOT DEFINED GIT_BRANCH OR GIT_BRANCH STREQUAL "") - set(IS_PHABRICATOR_URL FALSE) - set(url "https://raw.githubusercontent.com/MITK/MITK/master/CMake/MITKDashboardSetup.cmake") -else() - set(IS_PHABRICATOR_URL TRUE) - string(REPLACE "/" "%252F" GIT_BRANCH_URL ${GIT_BRANCH}) - set(url "https://phabricator.mitk.org/source/mitk/browse/${GIT_BRANCH_URL}/CMake/MITKDashboardSetup.cmake?view=raw") -endif() +set(url "https://raw.githubusercontent.com/MITK/MITK/${GIT_BRANCH}/CMake/MITKDashboardSetup.cmake") set(dest ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}.setup) downloadFile("${url}" "${dest}") include(${dest}) diff --git a/CMake/MITKDashboardSetup.cmake b/CMake/MITKDashboardSetup.cmake index eb41141d25..a1befe0885 100644 --- a/CMake/MITKDashboardSetup.cmake +++ b/CMake/MITKDashboardSetup.cmake @@ -1,145 +1,134 @@ # This file is intended to be included at the end of a custom MITKDashboardScript.TEMPLATE.cmake file list(APPEND CTEST_NOTES_FILES "${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}") # # Automatically determined properties # set(MY_OPERATING_SYSTEM ) if(UNIX) - # Download a utility script - # See T24757. - # if(IS_PHABRICATOR_URL) - # set(url "https://phabricator.mitk.org/source/mitk/browse/${GIT_BRANCH}/CMake/mitkDetectOS.sh?view=raw") - # else() - set(url "https://raw.githubusercontent.com/MITK/MITK/master/CMake/mitkDetectOS.sh") - # endif() + set(url "https://raw.githubusercontent.com/MITK/MITK/${GIT_BRANCH}/CMake/mitkDetectOS.sh") set(dest "${CTEST_SCRIPT_DIRECTORY}/mitkDetectOS.sh") downloadFile("${url}" "${dest}") execute_process(COMMAND sh "${dest}" RESULT_VARIABLE _result OUTPUT_VARIABLE _out OUTPUT_STRIP_TRAILING_WHITESPACE) if(NOT _result) set(MY_OPERATING_SYSTEM "${_out}") endif() endif() if(NOT MY_OPERATING_SYSTEM) set(MY_OPERATING_SYSTEM "${CMAKE_HOST_SYSTEM}") # Windows 7, Linux-2.6.32, Darwin... endif() site_name(CTEST_SITE) if(NOT DEFINED MITK_USE_Qt5) set(MITK_USE_Qt5 1) endif() # # Project specific properties # if(NOT CTEST_BUILD_NAME) if(MITK_USE_Qt5) set(CTEST_BUILD_NAME "${MY_OPERATING_SYSTEM} ${MY_COMPILER} ${CTEST_BUILD_CONFIGURATION}") else() set(CTEST_BUILD_NAME "${MY_OPERATING_SYSTEM} ${MY_COMPILER} ${CTEST_BUILD_CONFIGURATION}") endif() set(CTEST_BUILD_NAME "${CTEST_BUILD_NAME}${CTEST_BUILD_NAME_SUFFIX}") endif() set(PROJECT_BUILD_DIR "MITK-build") set(CTEST_PATH "$ENV{PATH}") if(WIN32) if("${CTEST_CMAKE_GENERATOR_PLATFORM}" STREQUAL "x64") set(CMAKE_LIBRARY_ARCHITECTURE x64) else() set(CMAKE_LIBRARY_ARCHITECTURE x86) endif() string(SUBSTRING "${MY_COMPILER}" 2 2 vc_version) set(OPENCV_BIN_DIR "${CTEST_BINARY_DIRECTORY}/ep/${CMAKE_LIBRARY_ARCHITECTURE}/vc${vc_version}/bin") set(BLUEBERRY_RUNTIME_DIR "${CTEST_BINARY_DIRECTORY}/MITK-build/bin/plugins/${CTEST_BUILD_CONFIGURATION}") set(PYTHON_BINARY_DIRS "${CTEST_BINARY_DIRECTORY}/ep/src/CTK-build/CMakeExternals/Install/bin") get_filename_component(_python_dir "${Python3_EXECUTABLE}" DIRECTORY) list(APPEND PYTHON_BINARY_DIRS "${_python_dir}") set(CTEST_PATH "${CTEST_PATH};${CTEST_BINARY_DIRECTORY}/ep/bin;${BLUEBERRY_RUNTIME_DIR};${OPENCV_BIN_DIR};${PYTHON_BINARY_DIRS}") endif() set(ENV{PATH} "${CTEST_PATH}") # If the dashscript doesn't define a GIT_REPOSITORY variable, let's define it here. if(NOT DEFINED GIT_REPOSITORY OR GIT_REPOSITORY STREQUAL "") set(GIT_REPOSITORY "https://phabricator.mitk.org/source/mitk.git") endif() # # Display build info # message("Site name: ${CTEST_SITE}") message("Build name: ${CTEST_BUILD_NAME}") message("Script Mode: ${SCRIPT_MODE}") message("Coverage: ${WITH_COVERAGE}, MemCheck: ${WITH_MEMCHECK}") # # Set initial cache options # if(CTEST_CMAKE_GENERATOR MATCHES ".*Makefiles.*") set(CTEST_USE_LAUNCHERS 1) else() set(CTEST_USE_LAUNCHERS 0) endif() set(ENV{CTEST_USE_LAUNCHERS_DEFAULT} ${CTEST_USE_LAUNCHERS}) if(NOT DEFINED MITK_BUILD_CONFIGURATION) set(MITK_BUILD_CONFIGURATION "All") endif() if(NOT DEFINED MITK_VTK_DEBUG_LEAKS) set(MITK_VTK_DEBUG_LEAKS 1) endif() set(INITIAL_CMAKECACHE_OPTIONS " SUPERBUILD_EXCLUDE_MITKBUILD_TARGET:BOOL=TRUE MITK_BUILD_CONFIGURATION:STRING=${MITK_BUILD_CONFIGURATION} MITK_VTK_DEBUG_LEAKS:BOOL=${MITK_VTK_DEBUG_LEAKS} ${ADDITIONAL_CMAKECACHE_OPTION} ") # Write a cache file for populating the MITK initial cache (not the superbuild cache). # This can be used to provide variables which are not passed through the # superbuild process to the MITK configure step) if(MITK_INITIAL_CACHE) set(mitk_cache_file "${CTEST_SCRIPT_DIRECTORY}/mitk_initial_cache.txt") file(WRITE "${mitk_cache_file}" "${MITK_INITIAL_CACHE}") set(INITIAL_CMAKECACHE_OPTIONS "${INITIAL_CMAKECACHE_OPTIONS} MITK_INITIAL_CACHE_FILE:INTERNAL=${mitk_cache_file} ") endif() if(MITK_EXTENSIONS) set(MITK_EXTENSION_DIRS "") foreach(extension ${MITK_EXTENSIONS}) if(extension MATCHES "[^|]+\\|[^|]+\\|(.+)") if(MITK_EXTENSION_DIRS) set(MITK_EXTENSION_DIRS "${MITK_EXTENSION_DIRS};") endif() set(MITK_EXTENSION_DIRS "${MITK_EXTENSION_DIRS}${CTEST_DASHBOARD_ROOT}/${CMAKE_MATCH_1}") endif() endforeach() endif() # # Download and include dashboard driver script # -if(IS_PHABRICATOR_URL) - string(REPLACE "/" "%252F" GIT_BRANCH_URL ${GIT_BRANCH}) - set(url "https://phabricator.mitk.org/source/mitk/browse/${GIT_BRANCH_URL}/CMake/MITKDashboardDriverScript.cmake?view=raw") -else() - set(url "https://raw.githubusercontent.com/MITK/MITK/master/CMake/MITKDashboardDriverScript.cmake") -endif() +set(url "https://raw.githubusercontent.com/MITK/MITK/${GIT_BRANCH}/CMake/MITKDashboardDriverScript.cmake") set(dest ${CTEST_SCRIPT_DIRECTORY}/${CTEST_SCRIPT_NAME}.driver) downloadFile("${url}" "${dest}") include(${dest}) diff --git a/CMake/mitkFunctionCreateModule.cmake b/CMake/mitkFunctionCreateModule.cmake index 5a4205ba7b..6e17f8a5ed 100644 --- a/CMake/mitkFunctionCreateModule.cmake +++ b/CMake/mitkFunctionCreateModule.cmake @@ -1,675 +1,677 @@ ################################################################## # # mitk_create_module # #! Creates a module for the automatic module dependency system within MITK. #! #! Example: #! #! \code #! mitk_create_module( #! DEPENDS PUBLIC MitkCore #! PACKAGE_DEPENDS #! PRIVATE Qt5|Xml+Networking #! PUBLIC ITK|Watershed #! \endcode #! #! The parameter specifies the name of the module which is used #! to create a logical target name. The parameter is optional in case the #! MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME variable evaluates to TRUE. The #! module name will then be derived from the directory name in which this #! function is called. #! #! If set, the following variables will be used to validate the module name: #! #! MITK_MODULE_NAME_REGEX_MATCH The module name must match this regular expression. #! MITK_MODULE_NAME_REGEX_NOT_MATCH The module name must not match this regular expression. #! #! If the MITK_MODULE_NAME_PREFIX variable is set, the module name will be prefixed #! with its contents. #! #! A modules source files are specified in a separate CMake file usually #! called files.cmake, located in the module root directory. The #! mitk_create_module() macro evaluates the following CMake variables #! from the files.cmake file: #! #! - CPP_FILES A list of .cpp files #! - H_FILES A list of .h files without a corresponding .cpp file #! - TXX_FILES A list of .txx files #! - RESOURCE_FILES A list of files (resources) which are embedded into the module #! - MOC_H_FILES A list of Qt header files which should be processed by the MOC #! - UI_FILES A list of .ui Qt UI files #! - QRC_FILES A list of .qrc Qt resource files #! - DOX_FILES A list of .dox Doxygen files #! #! List of variables available after the function is called: #! - MODULE_NAME #! - MODULE_TARGET #! - MODULE_IS_ENABLED #! - MODULE_SUBPROJECTS #! #! \sa mitk_create_executable #! #! Parameters (all optional): #! #! \param The module name (also used as target name) #! \param FILES_CMAKE File name of a CMake file setting source list variables #! (defaults to files.cmake) #! \param VERSION Module version number, e.g. "1.2.0" #! \param AUTOLOAD_WITH A module target name identifying the module which will #! trigger the automatic loading of this module #! \param DEPRECATED_SINCE Marks this modules as deprecated since #! \param DESCRIPTION A description for this module #! #! Multi-value Parameters (all optional): #! #! \param SUBPROJECTS List of CDash labels #! \param INCLUDE_DIRS Include directories for this module: #! \verbatim #! [[PUBLIC|PRIVATE|INTERFACE] ...]... #! \endverbatim #! The default scope for include directories is PUBLIC. #! \param DEPENDS List of module dependencies: #! \verbatim #! [[PUBLIC|PRIVATE|INTERFACE] ...]... #! \endverbatim #! The default scope for module dependencies is PUBLIC. #! \param PACKAGE_DEPENDS List of public packages dependencies (e.g. Qt, VTK, etc.). #! Package dependencies have the following syntax: #! \verbatim #! [PUBLIC|PRIVATE|INTERFACE] PACKAGE[|COMPONENT1[+COMPONENT2]...] #! \endverbatim #! The default scope for package dependencies is PRIVATE. #! \param ADDITIONAL_LIBS List of additional private libraries linked to this module. #! The folder containing the library will be added to the global list of library search paths. #! \param CPP_FILES List of source files for this module. If the list is non-empty, #! the module does not need to provide a files.cmake file or FILES_CMAKE argument. #! \param H_FILES List of public header files for this module. It is recommended to use #! a files.cmake file instead. #! #! Options (optional) #! #! \param FORCE_STATIC Force building this module as a static library #! \param GCC_DEFAULT_VISIBILITY Do not use gcc visibility flags - all #! symbols will be exported #! \param NO_INIT Do not create CppMicroServices initialization code #! \param NO_FEATURE_INFO Do not create a feature info by calling add_feature_info() #! \param WARNINGS_NO_ERRORS Do not treat compiler warnings as errors # ################################################################## function(mitk_create_module) set(_macro_params VERSION # module version number, e.g. "1.2.0" EXPORT_DEFINE # export macro name for public symbols of this module (DEPRECATED) AUTOLOAD_WITH # a module target name identifying the module which will trigger the # automatic loading of this module FILES_CMAKE # file name of a CMake file setting source list variables # (defaults to files.cmake) DEPRECATED_SINCE # marks this modules as deprecated DESCRIPTION # a description for this module ) set(_macro_multiparams SUBPROJECTS # list of CDash labels INCLUDE_DIRS # include directories: [PUBLIC|PRIVATE|INTERFACE] INTERNAL_INCLUDE_DIRS # include dirs internal to this module (DEPRECATED) DEPENDS # list of modules this module depends on: [PUBLIC|PRIVATE|INTERFACE] DEPENDS_INTERNAL # list of modules this module internally depends on (DEPRECATED) PACKAGE_DEPENDS # list of "packages this module depends on (e.g. Qt, VTK, etc.): [PUBLIC|PRIVATE|INTERFACE] TARGET_DEPENDS # list of CMake targets this module should depend on ADDITIONAL_LIBS # list of addidtional private libraries linked to this module. CPP_FILES # list of cpp files H_FILES # list of header files: [PUBLIC|PRIVATE] ) set(_macro_options FORCE_STATIC # force building this module as a static library HEADERS_ONLY # this module is a headers-only library GCC_DEFAULT_VISIBILITY # do not use gcc visibility flags - all symbols will be exported NO_DEFAULT_INCLUDE_DIRS # do not add default include directories like "include" or "." NO_INIT # do not create CppMicroServices initialization code NO_FEATURE_INFO # do not create a feature info by calling add_feature_info() WARNINGS_NO_ERRORS # do not treat compiler warnings as errors EXECUTABLE # create an executable; do not use directly, use mitk_create_executable() instead C_MODULE # compile all source files as C sources CXX_MODULE # compile all source files as C++ sources ) cmake_parse_arguments(MODULE "${_macro_options}" "${_macro_params}" "${_macro_multiparams}" ${ARGN}) set(MODULE_NAME ${MODULE_UNPARSED_ARGUMENTS}) # ----------------------------------------------------------------- # Sanity checks if(NOT MODULE_NAME) if(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME) get_filename_component(MODULE_NAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) else() message(SEND_ERROR "The module name must not be empty") endif() endif() set(_deprecated_args INTERNAL_INCLUDE_DIRS DEPENDS_INTERNAL EXPORT_DEFINE TARGET_DEPENDS HEADERS_ONLY) foreach(_deprecated_arg ${_deprecated_args}) if(MODULE_${_deprecated_arg}) message(WARNING "The ${_deprecated_arg} argument is deprecated") endif() endforeach() set(_module_type module) set(_Module_type Module) if(MODULE_EXECUTABLE) set(_module_type executable) set(_Module_type Executable) endif() if(MITK_MODULE_NAME_REGEX_MATCH) if(NOT ${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_MATCH}) message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" does not match the regular expression \"${MITK_MODULE_NAME_REGEX_MATCH}\".") endif() endif() if(MITK_MODULE_NAME_REGEX_NOT_MATCH) if(${MODULE_NAME} MATCHES ${MITK_MODULE_NAME_REGEX_NOT_MATCH}) message(SEND_ERROR "The ${_module_type} name \"${MODULE_NAME}\" must not match the regular expression \"${MITK_MODULE_NAME_REGEX_NOT_MATCH}\".") endif() endif() if(MITK_MODULE_NAME_PREFIX AND NOT MODULE_NAME MATCHES "^${MITK_MODULE_NAME_PREFIX}.*$") set(MODULE_NAME "${MITK_MODULE_NAME_PREFIX}${MODULE_NAME}") endif() if(NOT MODULE_FILES_CMAKE) set(MODULE_FILES_CMAKE files.cmake) endif() if(NOT IS_ABSOLUTE ${MODULE_FILES_CMAKE}) set(MODULE_FILES_CMAKE ${CMAKE_CURRENT_SOURCE_DIR}/${MODULE_FILES_CMAKE}) endif() if(NOT MODULE_SUBPROJECTS) if(MITK_DEFAULT_SUBPROJECTS) set(MODULE_SUBPROJECTS ${MITK_DEFAULT_SUBPROJECTS}) elseif(TARGET MITK-Modules) set(MODULE_SUBPROJECTS MITK-Modules) endif() endif() # check if the subprojects exist as targets if(MODULE_SUBPROJECTS) foreach(subproject ${MODULE_SUBPROJECTS}) if(NOT TARGET ${subproject}) message(SEND_ERROR "The subproject ${subproject} does not have a corresponding target") endif() endforeach() endif() # ----------------------------------------------------------------- # Check if module should be build set(MODULE_TARGET ${MODULE_NAME}) # assume worst case set(MODULE_IS_ENABLED 0) # first we check if we have an explicit module build list if(MITK_MODULES_TO_BUILD) list(FIND MITK_MODULES_TO_BUILD ${MODULE_NAME} _MOD_INDEX) if(_MOD_INDEX EQUAL -1) set(MODULE_IS_EXCLUDED 1) endif() endif() if(NOT MODULE_IS_EXCLUDED) # first of all we check for the dependencies _mitk_parse_package_args(${MODULE_PACKAGE_DEPENDS}) mitk_check_module_dependencies(MODULES ${MODULE_DEPENDS} PACKAGES ${PACKAGE_NAMES} MISSING_DEPENDENCIES_VAR _MISSING_DEP PACKAGE_DEPENDENCIES_VAR PACKAGE_NAMES) if(_MISSING_DEP) if(MODULE_NO_FEATURE_INFO) message("${_Module_type} ${MODULE_NAME} won't be built, missing dependency: ${_MISSING_DEP}") endif() set(MODULE_IS_ENABLED 0) else() foreach(dep ${MODULE_DEPENDS}) if(TARGET ${dep}) get_target_property(AUTLOAD_DEP ${dep} MITK_AUTOLOAD_DIRECTORY) if (AUTLOAD_DEP) message(SEND_ERROR "Module \"${MODULE_NAME}\" has an invalid dependency on autoload module \"${dep}\". Check MITK_CREATE_MODULE usage for \"${MODULE_NAME}\".") endif() endif() endforeach(dep) set(MODULE_IS_ENABLED 1) # now check for every package if it is enabled. This overlaps a bit with # MITK_CHECK_MODULE ... foreach(_package ${PACKAGE_NAMES}) if((DEFINED MITK_USE_${_package}) AND NOT (MITK_USE_${_package})) message("${_Module_type} ${MODULE_NAME} won't be built. Turn on MITK_USE_${_package} if you want to use it.") set(MODULE_IS_ENABLED 0) break() endif() endforeach() endif() endif() # ----------------------------------------------------------------- # Start creating the module if(MODULE_IS_ENABLED) # clear variables defined in files.cmake set(RESOURCE_FILES ) set(CPP_FILES ) set(H_FILES ) set(TXX_FILES ) set(DOX_FILES ) set(UI_FILES ) set(MOC_H_FILES ) set(QRC_FILES ) # clear other variables set(Q${KITNAME}_GENERATED_CPP ) set(Q${KITNAME}_GENERATED_MOC_CPP ) set(Q${KITNAME}_GENERATED_QRC_CPP ) set(Q${KITNAME}_GENERATED_UI_CPP ) # check and set-up auto-loading if(MODULE_AUTOLOAD_WITH) if(NOT TARGET "${MODULE_AUTOLOAD_WITH}") message(SEND_ERROR "The module target \"${MODULE_AUTOLOAD_WITH}\" specified as the auto-loading module for \"${MODULE_NAME}\" does not exist") endif() endif() set(_module_autoload_meta_target "${CMAKE_PROJECT_NAME}-autoload") # create a meta-target if it does not already exist if(NOT TARGET ${_module_autoload_meta_target}) add_custom_target(${_module_autoload_meta_target}) set_property(TARGET ${_module_autoload_meta_target} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules/Autoload") endif() if(NOT MODULE_EXPORT_DEFINE) set(MODULE_EXPORT_DEFINE ${MODULE_NAME}_EXPORT) endif() if(MITK_GENERATE_MODULE_DOT) message("MODULEDOTNAME ${MODULE_NAME}") foreach(dep ${MODULE_DEPENDS}) message("MODULEDOT \"${MODULE_NAME}\" -> \"${dep}\" ; ") endforeach(dep) endif(MITK_GENERATE_MODULE_DOT) if (EXISTS ${MODULE_FILES_CMAKE}) include(${MODULE_FILES_CMAKE}) endif() if(MODULE_CPP_FILES) list(APPEND CPP_FILES ${MODULE_CPP_FILES}) endif() if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/src") # Preprend the "src" directory to the cpp file list set(_cpp_files ${CPP_FILES}) set(CPP_FILES ) foreach(_cpp_file ${_cpp_files}) list(APPEND CPP_FILES "src/${_cpp_file}") endforeach() endif() if(CPP_FILES OR RESOURCE_FILES OR UI_FILES OR MOC_H_FILES OR QRC_FILES) set(MODULE_HEADERS_ONLY 0) if(MODULE_C_MODULE) set_source_files_properties(${CPP_FILES} PROPERTIES LANGUAGE C) elseif(MODULE_CXX_MODULE) set_source_files_properties(${CPP_FILES} PROPERTIES LANGUAGE CXX) endif() else() set(MODULE_HEADERS_ONLY 1) if(MODULE_AUTOLOAD_WITH) message(SEND_ERROR "A headers only module cannot be auto-loaded") endif() endif() set(module_c_flags ) set(module_c_flags_debug ) set(module_c_flags_release ) set(module_cxx_flags ) set(module_cxx_flags_debug ) set(module_cxx_flags_release ) if(MODULE_GCC_DEFAULT_VISIBILITY OR NOT CMAKE_COMPILER_IS_GNUCXX) # We only support hidden visibility for gcc for now. Clang still has troubles with # correctly marking template declarations and explicit template instantiations as exported. # See http://comments.gmane.org/gmane.comp.compilers.clang.scm/50028 # and http://llvm.org/bugs/show_bug.cgi?id=10113 set(CMAKE_CXX_VISIBILITY_PRESET default) set(CMAKE_VISIBILITY_INLINES_HIDDEN 0) else() set(CMAKE_CXX_VISIBILITY_PRESET hidden) set(CMAKE_VISIBILITY_INLINES_HIDDEN 1) endif() if(NOT MODULE_WARNINGS_NO_ERRORS) if(MSVC_VERSION) mitkFunctionCheckCAndCXXCompilerFlags("/WX" module_c_flags module_cxx_flags) # this would turn on unused parameter warnings, but unfortunately MSVC cannot # distinguish yet between internal and external headers so this would be triggered # a lot by external code. There is support for it on the way so this line could be # reactivated after https://gitlab.kitware.com/cmake/cmake/issues/17904 has been fixed. # mitkFunctionCheckCAndCXXCompilerFlags("/w34100" module_c_flags module_cxx_flags) else() mitkFunctionCheckCAndCXXCompilerFlags(-Werror module_c_flags module_cxx_flags) # The flag "c++0x-static-nonintegral-init" has been renamed in newer Clang # versions to "static-member-init", see # http://clang-developers.42468.n3.nabble.com/Wc-0x-static-nonintegral-init-gone-td3999651.html # # Also, older Clang and seemingly all gcc versions do not warn if unknown # "-no-*" flags are used, so CMake will happily append any -Wno-* flag to the # command line. This may get confusing if unrelated compiler errors happen and # the error output then additionally contains errors about unknown flags (which # is not the case if there were no compile errors). # # So instead of using -Wno-* we use -Wno-error=*, which will be properly rejected by # the compiler and if applicable, prints the specific warning as a real warning and # not as an error (although -Werror was given). mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=c++0x-static-nonintegral-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=static-member-init" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=unknown-warning" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=gnu" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=class-memaccess" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=inconsistent-missing-override" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated-copy" module_c_flags module_cxx_flags) mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=cast-function-type" module_c_flags module_cxx_flags) + mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=deprecated-declarations" module_c_flags module_cxx_flags) + mitkFunctionCheckCAndCXXCompilerFlags("-Wno-error=type-limits" module_c_flags module_cxx_flags) endif() endif() if(MODULE_FORCE_STATIC) set(_STATIC STATIC) else() set(_STATIC ) endif(MODULE_FORCE_STATIC) if(NOT MODULE_HEADERS_ONLY) if(NOT MODULE_NO_INIT OR RESOURCE_FILES) find_package(CppMicroServices QUIET NO_MODULE REQUIRED) endif() if(NOT MODULE_NO_INIT) usFunctionGenerateModuleInit(CPP_FILES) endif() set(binary_res_files ) set(source_res_files ) if(RESOURCE_FILES) if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/resource") set(res_dir resource) elseif(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/Resources") set(res_dir Resources) else() message(SEND_ERROR "Resources specified but ${CMAKE_CURRENT_SOURCE_DIR}/resource directory not found.") endif() foreach(res_file ${RESOURCE_FILES}) if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/${res_dir}/${res_file}) list(APPEND binary_res_files "${res_file}") else() list(APPEND source_res_files "${res_file}") endif() endforeach() # Add a source level dependencies on resource files usFunctionGetResourceSource(TARGET ${MODULE_TARGET} OUT CPP_FILES) endif() endif() if(MITK_USE_Qt5) if(UI_FILES) qt5_wrap_ui(Q${KITNAME}_GENERATED_UI_CPP ${UI_FILES}) endif() if(MOC_H_FILES) qt5_wrap_cpp(Q${KITNAME}_GENERATED_MOC_CPP ${MOC_H_FILES} OPTIONS -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION) endif() if(QRC_FILES) qt5_add_resources(Q${KITNAME}_GENERATED_QRC_CPP ${QRC_FILES}) endif() endif() set(Q${KITNAME}_GENERATED_CPP ${Q${KITNAME}_GENERATED_CPP} ${Q${KITNAME}_GENERATED_UI_CPP} ${Q${KITNAME}_GENERATED_MOC_CPP} ${Q${KITNAME}_GENERATED_QRC_CPP}) mitkFunctionOrganizeSources( SOURCE ${CPP_FILES} HEADER ${H_FILES} TXX ${TXX_FILES} DOC ${DOX_FILES} UI ${UI_FILES} QRC ${QRC_FILES} MOC ${Q${KITNAME}_GENERATED_MOC_CPP} GEN_QRC ${Q${KITNAME}_GENERATED_QRC_CPP} GEN_UI ${Q${KITNAME}_GENERATED_UI_CPP} ) set(coverage_sources ${CPP_FILES} ${H_FILES} ${GLOBBED__H_FILES} ${CORRESPONDING__H_FILES} ${TXX_FILES} ${TOOL_CPPS} ${TOOL_GUI_CPPS}) if(MODULE_SUBPROJECTS) set_property(SOURCE ${coverage_sources} APPEND PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) endif() # --------------------------------------------------------------- # Create the actual module target if(MODULE_HEADERS_ONLY) add_library(${MODULE_TARGET} INTERFACE) # INTERFACE_LIBRARY targets may only have whitelisted properties. The property "FOLDER" is not allowed. # set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules") else() if(MODULE_EXECUTABLE) add_executable(${MODULE_TARGET} ${MODULE_CPP_FILES} ${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP} ${DOX_FILES} ${UI_FILES} ${QRC_FILES}) set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules/Executables") set(_us_module_name main) else() add_library(${MODULE_TARGET} ${_STATIC} ${coverage_sources} ${CPP_FILES_GENERATED} ${Q${KITNAME}_GENERATED_CPP} ${DOX_FILES} ${UI_FILES} ${QRC_FILES}) set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules") set(_us_module_name ${MODULE_TARGET}) endif() # Apply properties to the module target. target_compile_definitions(${MODULE_TARGET} PRIVATE US_MODULE_NAME=${_us_module_name}) if(MODULE_C_MODULE) if(module_c_flags) string(REPLACE " " ";" module_c_flags "${module_c_flags}") target_compile_options(${MODULE_TARGET} PRIVATE ${module_c_flags}) endif() if(module_c_flags_debug) string(REPLACE " " ";" module_c_flags_debug "${module_c_flags_debug}") target_compile_options(${MODULE_TARGET} PRIVATE $<$:${module_c_flags_debug}>) endif() if(module_c_flags_release) string(REPLACE " " ";" module_c_flags_release "${module_c_flags_release}") target_compile_options(${MODULE_TARGET} PRIVATE $<$:${module_c_flags_release}>) endif() else() if(module_cxx_flags) string(REPLACE " " ";" module_cxx_flags "${module_cxx_flags}") target_compile_options(${MODULE_TARGET} PRIVATE ${module_cxx_flags}) endif() if(module_cxx_flags_debug) string(REPLACE " " ";" module_cxx_flags_debug "${module_cxx_flags_debug}") target_compile_options(${MODULE_TARGET} PRIVATE $<$:${module_cxx_flags_debug}>) endif() if(module_cxx_flags_release) string(REPLACE " " ";" module_cxx_flags_release "${module_cxx_flags_release}") target_compile_options(${MODULE_TARGET} PRIVATE $<$:${module_cxx_flags_release}>) endif() endif() set_property(TARGET ${MODULE_TARGET} PROPERTY US_MODULE_NAME ${_us_module_name}) # Add additional library search directories to a global property which # can be evaluated by other CMake macros, e.g. our install scripts. if(MODULE_ADDITIONAL_LIBS) target_link_libraries(${MODULE_TARGET} PRIVATE ${MODULE_ADDITIONAL_LIBS}) get_property(_mitk_additional_library_search_paths GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) foreach(_lib_filepath ${MODULE_ADDITIONAL_LIBS}) get_filename_component(_search_path "${_lib_filepath}" PATH) if(_search_path) list(APPEND _mitk_additional_library_search_paths "${_search_path}") endif() endforeach() if(_mitk_additional_library_search_paths) list(REMOVE_DUPLICATES _mitk_additional_library_search_paths) set_property(GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS ${_mitk_additional_library_search_paths}) endif() endif() # add the target name to a global property which is used in the top-level # CMakeLists.txt file to export the target set_property(GLOBAL APPEND PROPERTY MITK_MODULE_TARGETS ${MODULE_TARGET}) if(MODULE_AUTOLOAD_WITH) # for auto-loaded modules, adapt the output directory add_dependencies(${_module_autoload_meta_target} ${MODULE_TARGET}) if(WIN32) set(_module_output_prop RUNTIME_OUTPUT_DIRECTORY) else() set(_module_output_prop LIBRARY_OUTPUT_DIRECTORY) endif() set(_module_output_dir ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) get_target_property(_module_is_imported ${MODULE_AUTOLOAD_WITH} IMPORTED) if(NOT _module_is_imported) # if the auto-loading module is not imported, get its location # and put the auto-load module relative to it. get_target_property(_module_output_dir ${MODULE_AUTOLOAD_WITH} ${_module_output_prop}) set_target_properties(${MODULE_TARGET} PROPERTIES ${_module_output_prop} ${_module_output_dir}/${MODULE_AUTOLOAD_WITH}) else() set_target_properties(${MODULE_TARGET} PROPERTIES ${_module_output_prop} ${CMAKE_${_module_output_prop}}/${MODULE_AUTOLOAD_WITH}) endif() set_target_properties(${MODULE_TARGET} PROPERTIES MITK_AUTOLOAD_DIRECTORY ${MODULE_AUTOLOAD_WITH}) # add the auto-load module name as a property set_property(TARGET ${MODULE_AUTOLOAD_WITH} APPEND PROPERTY MITK_AUTOLOAD_TARGETS ${MODULE_TARGET}) endif() if(binary_res_files) usFunctionAddResources(TARGET ${MODULE_TARGET} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${res_dir} FILES ${binary_res_files}) endif() if(source_res_files) usFunctionAddResources(TARGET ${MODULE_TARGET} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${res_dir} FILES ${source_res_files}) endif() if(binary_res_files OR source_res_files) usFunctionEmbedResources(TARGET ${MODULE_TARGET}) endif() if(MODULE_DEPRECATED_SINCE) set_property(TARGET ${MODULE_TARGET} PROPERTY MITK_MODULE_DEPRECATED_SINCE ${MODULE_DEPRECATED_SINCE}) endif() # create export macros if (NOT MODULE_EXECUTABLE) set(_export_macro_name ) if(MITK_LEGACY_EXPORT_MACRO_NAME) set(_export_macro_names EXPORT_MACRO_NAME ${MODULE_EXPORT_DEFINE} NO_EXPORT_MACRO_NAME ${MODULE_NAME}_NO_EXPORT DEPRECATED_MACRO_NAME ${MODULE_NAME}_DEPRECATED NO_DEPRECATED_MACRO_NAME ${MODULE_NAME}_NO_DEPRECATED ) endif() generate_export_header(${MODULE_NAME} ${_export_macro_names} EXPORT_FILE_NAME ${MODULE_NAME}Exports.h ) endif() target_include_directories(${MODULE_TARGET} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) endif() # --------------------------------------------------------------- # Properties for both header-only and compiled modules if(MODULE_HEADERS_ONLY) set(_module_property_type INTERFACE) else() set(_module_property_type PUBLIC) endif() if(MODULE_TARGET_DEPENDS) add_dependencies(${MODULE_TARGET} ${MODULE_TARGET_DEPENDS}) endif() if(MODULE_SUBPROJECTS AND NOT MODULE_HEADERS_ONLY) set_property(TARGET ${MODULE_TARGET} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) foreach(subproject ${MODULE_SUBPROJECTS}) add_dependencies(${subproject} ${MODULE_TARGET}) endforeach() endif() set(DEPENDS "${MODULE_DEPENDS}") if(NOT MODULE_NO_INIT AND NOT MODULE_HEADERS_ONLY) # Add a CppMicroServices dependency implicitly, since it is # needed for the generated "module initialization" code. set(DEPENDS "CppMicroServices;${DEPENDS}") endif() if(DEPENDS OR MODULE_PACKAGE_DEPENDS) mitk_use_modules(TARGET ${MODULE_TARGET} MODULES ${DEPENDS} PACKAGES ${MODULE_PACKAGE_DEPENDS} ) endif() if(NOT MODULE_C_MODULE) target_compile_features(${MODULE_TARGET} ${_module_property_type} ${MITK_CXX_FEATURES}) endif() # add include directories if(MODULE_INTERNAL_INCLUDE_DIRS) target_include_directories(${MODULE_TARGET} PRIVATE ${MODULE_INTERNAL_INCLUDE_DIRS}) endif() if(NOT MODULE_NO_DEFAULT_INCLUDE_DIRS) if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/include) target_include_directories(${MODULE_TARGET} ${_module_property_type} include) else() target_include_directories(${MODULE_TARGET} ${_module_property_type} .) endif() if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/src) target_include_directories(${MODULE_TARGET} PRIVATE src) endif() endif() target_include_directories(${MODULE_TARGET} ${_module_property_type} ${MODULE_INCLUDE_DIRS}) endif() # ----------------------------------------------------------------- # Record missing dependency information if(_MISSING_DEP) if(MODULE_DESCRIPTION) set(MODULE_DESCRIPTION "${MODULE_DESCRIPTION} (missing dependencies: ${_MISSING_DEP})") else() set(MODULE_DESCRIPTION "(missing dependencies: ${_MISSING_DEP})") endif() endif() if(NOT MODULE_NO_FEATURE_INFO) add_feature_info(${MODULE_NAME} MODULE_IS_ENABLED "${MODULE_DESCRIPTION}") endif() set(MODULE_NAME ${MODULE_NAME} PARENT_SCOPE) set(MODULE_TARGET ${MODULE_TARGET} PARENT_SCOPE) set(MODULE_IS_ENABLED ${MODULE_IS_ENABLED} PARENT_SCOPE) set(MODULE_SUBPROJECTS ${MODULE_SUBPROJECTS} PARENT_SCOPE) endfunction() diff --git a/CMakeExternals/ITK.cmake b/CMakeExternals/ITK.cmake index c958c56146..6217b38384 100644 --- a/CMakeExternals/ITK.cmake +++ b/CMakeExternals/ITK.cmake @@ -1,85 +1,85 @@ #----------------------------------------------------------------------------- # ITK #----------------------------------------------------------------------------- # Sanity checks if(DEFINED ITK_DIR AND NOT EXISTS ${ITK_DIR}) message(FATAL_ERROR "ITK_DIR variable is defined but corresponds to non-existing directory") endif() set(proj ITK) set(proj_DEPENDENCIES GDCM) if(MITK_USE_OpenCV) list(APPEND proj_DEPENDENCIES OpenCV) endif() if(MITK_USE_HDF5) list(APPEND proj_DEPENDENCIES HDF5) endif() set(ITK_DEPENDS ${proj}) if(NOT DEFINED ITK_DIR) set(additional_cmake_args -DUSE_WRAP_ITK:BOOL=OFF) if(MITK_USE_OpenCV) list(APPEND additional_cmake_args -DModule_ITKVideoBridgeOpenCV:BOOL=ON -DOpenCV_DIR:PATH=${OpenCV_DIR} ) endif() # Keep the behaviour of ITK 4.3 which by default turned on ITK Review # see MITK bug #17338 list(APPEND additional_cmake_args -DModule_ITKReview:BOOL=ON # for 4.7, the OpenJPEG is needed by review but the variable must be set -DModule_ITKOpenJPEG:BOOL=ON # Added Module for Wavelets -DModule_IsotropicWavelets:BOOL=ON ) if(CTEST_USE_LAUNCHERS) list(APPEND additional_cmake_args "-DCMAKE_PROJECT_${proj}_INCLUDE:FILEPATH=${CMAKE_ROOT}/Modules/CTestUseLaunchers.cmake" ) endif() mitk_query_custom_ep_vars() ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} UPDATE_COMMAND "" - # ITK 4.13.2 + GCC9 patch - URL ${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/ITK_a092294.tar.gz - URL_MD5 5e3f39105917d992d5079be473994bc6 + # ITK 4.13 release branch snapshot + URL https://github.com/InsightSoftwareConsortium/ITK/archive/e53d1d94.tar.gz + URL_MD5 977f77cb299cf3d722d13dd5408bcde5 CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_ARGS ${ep_common_args} ${additional_cmake_args} -DBUILD_EXAMPLES:BOOL=OFF -DITK_USE_SYSTEM_GDCM:BOOL=ON -DGDCM_DIR:PATH=${GDCM_DIR} -DITK_USE_SYSTEM_HDF5:BOOL=ON -DHDF5_DIR:PATH=${HDF5_DIR} ${${proj}_CUSTOM_CMAKE_ARGS} CMAKE_CACHE_ARGS ${ep_common_cache_args} ${${proj}_CUSTOM_CMAKE_CACHE_ARGS} CMAKE_CACHE_DEFAULT_ARGS ${ep_common_cache_default_args} ${${proj}_CUSTOM_CMAKE_CACHE_DEFAULT_ARGS} DEPENDS ${proj_DEPENDENCIES} ) set(ITK_DIR ${ep_prefix}) mitkFunctionInstallExternalCMakeProject(${proj}) else() mitkMacroEmptyExternalProject(${proj} "${proj_DEPENDENCIES}") endif() diff --git a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step08.dox b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step08.dox index f5e9f8a3a3..d4de241f01 100644 --- a/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step08.dox +++ b/Documentation/Doxygen/3-DeveloperManual/Starting/GettingToKnow/Tutorial/Step08.dox @@ -1,25 +1,25 @@ /** \page Step08Page MITK Tutorial - Step 8: Use QmitkStdMultiWidget as widget \li \ref Step8.cpp "Step8.cpp"\n \li \ref Step8.h "Step8.h"\n \li \ref Step8main.cpp "Step8main.cpp"\n \li Path to files used in this step: \n http://mitk.org/download/tutorial-data/Pic3D.nrrd (image) In this step a QmitkStdMultiWidget is used. It offers four views of the data. From top left to bottom left the views are initialized as axial, sagittal and coronar. The bottom right view is initialized as 3D view. \image html step8_result.png Step8 inherits from Step6. The method SetupWidgets() is changed: A QmitkStdMultiWidget is used instead of one QmitkRenderWindow and two instances of QmitkSliceWidget. \dontinclude Step8.cpp \skipline Part Ia - \until SetWidgetPlanesVisibility + \until levelWindowWidget->SetDataStorage(m_DataStorage); \ref Step07Page "[Previous step]" \ref Step09Page "[Next step]" */ diff --git a/Examples/Tutorial/Step8/Step8.cpp b/Examples/Tutorial/Step8/Step8.cpp index 86f1c18571..696ba0b9c7 100644 --- a/Examples/Tutorial/Step8/Step8.cpp +++ b/Examples/Tutorial/Step8/Step8.cpp @@ -1,75 +1,77 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "Step8.h" +#include "QmitkLevelWindowWidget.h" #include "QmitkRenderWindow.h" #include "QmitkStdMultiWidget.h" #include "mitkRenderingManager.h" #include #include //##Documentation //## @brief As Step6, but with QmitkStdMultiWidget as widget Step8::Step8(int argc, char *argv[], QWidget *parent) : Step6(argc, argv, parent) { } void Step8::SetupWidgets() { //************************************************************************* // Part I: Create windows and pass the tree to it //************************************************************************* // Create toplevel widget with vertical layout QVBoxLayout *vlayout = new QVBoxLayout(this); vlayout->setMargin(0); vlayout->setSpacing(2); // Create viewParent widget with horizontal layout QWidget *viewParent = new QWidget(this); vlayout->addWidget(viewParent); QHBoxLayout *hlayout = new QHBoxLayout(viewParent); hlayout->setMargin(0); //************************************************************************* // Part Ia: create and initialize QmitkStdMultiWidget //************************************************************************* QmitkStdMultiWidget *multiWidget = new QmitkStdMultiWidget(viewParent); hlayout->addWidget(multiWidget); // Tell the multiWidget which DataStorage to render multiWidget->SetDataStorage(m_DataStorage); - // Initialize views as axial, sagittal, coronar (from - // top-left to bottom) - auto geo = m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll()); - mitk::RenderingManager::GetInstance()->InitializeViews(geo); - - // Initialize bottom-right view as 3D view - multiWidget->GetRenderWindow4()->GetRenderer()->SetMapperID(mitk::BaseRenderer::Standard3D); + // Initialize the multiWidget with the render windows + multiWidget->InitializeMultiWidget(); // Add the displayed views to the DataStorage to see their positions in 2D and 3D - multiWidget->AddDisplayPlaneSubTree(); multiWidget->AddPlanesToDataStorage(); multiWidget->SetWidgetPlanesVisibility(true); //************************************************************************* - // Part II: Setup standard interaction with the mouse + // Part Ib: create and initialize LevelWindowWidget //************************************************************************* + QmitkLevelWindowWidget *levelWindowWidget = new QmitkLevelWindowWidget(viewParent); + + hlayout->addWidget(levelWindowWidget); + + // Tell the levelWindowWidget which DataStorage to access + levelWindowWidget->SetDataStorage(m_DataStorage); + } /** \example Step8.cpp */ diff --git a/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp b/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp index 323c74bc00..87269991c4 100644 --- a/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp +++ b/Modules/BasicImageProcessing/src/mitkTransformationOperation.cpp @@ -1,500 +1,500 @@ /*============================================================================ 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 "mitkTransformationOperation.h" #include #include #include #include #include #include // Wavelet #include #include #include #include #include #include #include #include #include #include "itkZeroFluxNeumannBoundaryCondition.h" #include "itkPeriodicBoundaryCondition.h" #include "itkConstantBoundaryCondition.h" //#include #include "itkCastImageFilter.h" #include "itkUnaryFunctorImageFilter.h" #include -#include +#include #include namespace mitk { namespace Functor { template< class TInput> class ThresholdValue { public: ThresholdValue() {}; ~ThresholdValue() {}; bool operator!=(const ThresholdValue &) const { return false; } bool operator==(const ThresholdValue & other) const { return !(*this != other); } inline unsigned short operator()(const TInput & A) const { if (A < value) return 0; else return 1; } double value = 0.0; }; template< class TInput, class TOutput> class RoundValue { public: RoundValue() {}; ~RoundValue() {}; bool operator!=(const RoundValue &) const { return false; } bool operator==(const RoundValue & other) const { return !(*this != other); } inline TOutput operator()(const TInput & A) const { return std::round(A); } }; } } template static void ExecuteMultiResolution(itk::Image* image, unsigned int numberOfLevels, bool outputAsDouble, std::vector &resultImages) { typedef itk::Image ImageType; typedef itk::Image DoubleOutputType; typedef itk::RecursiveMultiResolutionPyramidImageFilter ImageTypeFilterType; typedef itk::RecursiveMultiResolutionPyramidImageFilter DoubleTypeFilterType; if (outputAsDouble) { typename DoubleTypeFilterType::Pointer recursiveMultiResolutionPyramidImageFilter = DoubleTypeFilterType::New(); recursiveMultiResolutionPyramidImageFilter->SetInput(image); recursiveMultiResolutionPyramidImageFilter->SetNumberOfLevels(numberOfLevels); recursiveMultiResolutionPyramidImageFilter->Update(); // This outputs the levels (0 is the lowest resolution) for (unsigned int i = 0; i < numberOfLevels; ++i) { mitk::Image::Pointer outputImage = mitk::Image::New(); CastToMitkImage(recursiveMultiResolutionPyramidImageFilter->GetOutput(i), outputImage); resultImages.push_back(outputImage); } } else { typename ImageTypeFilterType::Pointer recursiveMultiResolutionPyramidImageFilter = ImageTypeFilterType::New(); recursiveMultiResolutionPyramidImageFilter->SetInput(image); recursiveMultiResolutionPyramidImageFilter->SetNumberOfLevels(numberOfLevels); recursiveMultiResolutionPyramidImageFilter->Update(); // This outputs the levels (0 is the lowest resolution) for (unsigned int i = 0; i < numberOfLevels; ++i) { mitk::Image::Pointer outputImage = mitk::Image::New(); CastToMitkImage(recursiveMultiResolutionPyramidImageFilter->GetOutput(i), outputImage); resultImages.push_back(outputImage); } } } std::vector mitk::TransformationOperation::MultiResolution(Image::Pointer & image, unsigned int numberOfLevels, bool outputAsDouble) { std::vector resultImages; AccessByItk_n(image, ExecuteMultiResolution, (numberOfLevels, outputAsDouble, resultImages)); return resultImages; } template static void ExecuteLaplacianOfGaussian(itk::Image* image, double sigma, bool outputAsDouble, mitk::Image::Pointer &resultImage) { typedef itk::Image ImageType; typedef itk::Image DoubleOutputType; typedef itk::LaplacianRecursiveGaussianImageFilter ImageTypeFilterType; typedef itk::LaplacianRecursiveGaussianImageFilter DoubleTypeFilterType; if (outputAsDouble) { typename DoubleTypeFilterType::Pointer filter = DoubleTypeFilterType::New(); filter->SetInput(image); filter->SetSigma(sigma); filter->Update(); CastToMitkImage(filter->GetOutput(), resultImage); } else { typename ImageTypeFilterType::Pointer filter = ImageTypeFilterType::New(); filter->SetInput(image); filter->SetSigma(sigma); filter->Update(); CastToMitkImage(filter->GetOutput(), resultImage); } } mitk::Image::Pointer mitk::TransformationOperation::LaplacianOfGaussian(Image::Pointer & image, double sigma, bool outputAsDouble) { Image::Pointer resultImage; AccessByItk_n(image, ExecuteLaplacianOfGaussian, (sigma, outputAsDouble, resultImage)); return resultImage; } template static void ExecuteSpecificWaveletTransformation(itk::Image* image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, std::vector &resultImages) { const unsigned int Dimension = VImageDimension; typedef TInputPixel PixelType; typedef TOutputPixel OutputPixelType; typedef itk::Image< PixelType, Dimension > ImageType; typedef itk::Image< double, Dimension > DoubleImageType; typedef itk::Image< OutputPixelType, Dimension > OutputImageType; typedef itk::CastImageFilter< ImageType, DoubleImageType > CastFilterType; typedef itk::FFTPadPositiveIndexImageFilter< DoubleImageType > FFTPadType; typedef itk::ForwardFFTImageFilter< DoubleImageType, itk::Image< std::complex, Dimension> > FFTFilterType; typedef typename FFTFilterType::OutputImageType ComplexImageType; typedef TWaveletFunction WaveletFunctionType; typedef itk::WaveletFrequencyFilterBankGenerator< ComplexImageType, WaveletFunctionType > WaveletFilterBankType; typedef itk::WaveletFrequencyForward< ComplexImageType, ComplexImageType, WaveletFilterBankType > ForwardWaveletType; typedef itk::InverseFFTImageFilter< ComplexImageType, OutputImageType > InverseFFTFilterType; // Convert input parameter unsigned int highSubBands = numberOfBands; //inputBands; unsigned int levels = numberOfLevels; // Perform FFT on input image typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(image); // Pad Image so it fits the expect typename FFTPadType::Pointer fftpad = FFTPadType::New(); fftpad->SetSizeGreatestPrimeFactor(4); itk::ConstantBoundaryCondition< DoubleImageType > constantBoundaryCondition; itk::PeriodicBoundaryCondition< DoubleImageType > periodicBoundaryCondition; itk::ZeroFluxNeumannBoundaryCondition< DoubleImageType > zeroFluxNeumannBoundaryCondition; switch (condition) { case mitk::BorderCondition::Constant: fftpad->SetBoundaryCondition(&constantBoundaryCondition); break; case mitk::BorderCondition::Periodic: fftpad->SetBoundaryCondition(&periodicBoundaryCondition); break; case mitk::BorderCondition::ZeroFluxNeumann: fftpad->SetBoundaryCondition(&zeroFluxNeumannBoundaryCondition); break; default: break; } fftpad->SetInput(castFilter->GetOutput()); typename FFTFilterType::Pointer fftFilter = FFTFilterType::New(); fftFilter->SetInput(fftpad->GetOutput()); // Calculate forward transformation typename ForwardWaveletType::Pointer forwardWavelet = ForwardWaveletType::New(); forwardWavelet->SetHighPassSubBands(highSubBands); forwardWavelet->SetLevels(levels); forwardWavelet->SetInput(fftFilter->GetOutput()); forwardWavelet->Update(); // Obtain target spacing, size and origin typename ComplexImageType::SpacingType inputSpacing; for (unsigned int i = 0; i < Dimension; ++i) { inputSpacing[i] = image->GetLargestPossibleRegion().GetSize()[i]; } typename ComplexImageType::SpacingType expectedSpacing = inputSpacing; typename ComplexImageType::PointType inputOrigin = image->GetOrigin(); typename ComplexImageType::PointType expectedOrigin = inputOrigin; typename ComplexImageType::SizeType inputSize = fftFilter->GetOutput()->GetLargestPossibleRegion().GetSize(); typename ComplexImageType::SizeType expectedSize = inputSize; // Inverse FFT to obtain filtered images typename InverseFFTFilterType::Pointer inverseFFT = InverseFFTFilterType::New(); for (unsigned int level = 0; level < numberOfLevels + 1; ++level) { double scaleFactorPerLevel = std::pow(static_cast< double >(forwardWavelet->GetScaleFactor()),static_cast< double >(level)); for (unsigned int i = 0; i < Dimension; ++i) { expectedSize[i] = inputSize[i] / scaleFactorPerLevel; expectedOrigin[i] = inputOrigin[i]; expectedSpacing[i] = inputSpacing[i] * scaleFactorPerLevel; } for (unsigned int band = 0; band < highSubBands; ++band) { unsigned int nOutput = level * forwardWavelet->GetHighPassSubBands() + band; // Do not compute bands in low-pass level. if (level == numberOfLevels && band == 0) { nOutput = forwardWavelet->GetTotalOutputs() - 1; } else if (level == numberOfLevels && band != 0) { break; } inverseFFT->SetInput(forwardWavelet->GetOutput(nOutput)); inverseFFT->Update(); auto itkOutputImage = inverseFFT->GetOutput(); itkOutputImage->SetSpacing(expectedSpacing); mitk::Image::Pointer outputImage = mitk::Image::New(); CastToMitkImage(itkOutputImage, outputImage); resultImages.push_back(outputImage); } } } template static void ExecuteWaveletTransformation(itk::Image* image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, mitk::WaveletType waveletType, std::vector &resultImages) { typedef itk::Point< double, VImageDimension > PointType; typedef itk::HeldIsotropicWavelet< double, VImageDimension, PointType > HeldIsotropicWaveletType; typedef itk::VowIsotropicWavelet< double, VImageDimension, PointType > VowIsotropicWaveletType; typedef itk::SimoncelliIsotropicWavelet< double, VImageDimension, PointType > SimoncelliIsotropicWaveletType; typedef itk::ShannonIsotropicWavelet< double, VImageDimension, PointType > ShannonIsotropicWaveletType; switch (waveletType) { case mitk::WaveletType::Held: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; case mitk::WaveletType::Shannon: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; case mitk::WaveletType::Simoncelli: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; case mitk::WaveletType::Vow: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; default: ExecuteSpecificWaveletTransformation(image, numberOfLevels, numberOfBands, condition, resultImages); break; } } std::vector mitk::TransformationOperation::WaveletForward(Image::Pointer & image, unsigned int numberOfLevels, unsigned int numberOfBands, mitk::BorderCondition condition, mitk::WaveletType waveletType) { std::vector resultImages; AccessByItk_n(image, ExecuteWaveletTransformation, (numberOfLevels, numberOfBands, condition, waveletType, resultImages)); return resultImages; } template static void ExecuteImageTypeToDouble(itk::Image* image, mitk::Image::Pointer &outputImage) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< double, VImageDimension > DoubleImageType; typedef itk::CastImageFilter< ImageType, DoubleImageType > CastFilterType; typedef itk::ImageDuplicator< DoubleImageType > DuplicatorType; // Perform FFT on input image typename CastFilterType::Pointer castFilter = CastFilterType::New(); castFilter->SetInput(image); castFilter->Update(); typename DuplicatorType::Pointer duplicator = DuplicatorType::New(); duplicator->SetInputImage(castFilter->GetOutput()); duplicator->Update(); CastToMitkImage(duplicator->GetOutput(), outputImage); } template static void ExecuteRoundImage(itk::Image* /*image*/, mitk::Image::Pointer resampledImage, mitk::Image::Pointer &outputImage) { typedef itk::Image< TPixel, VImageDimension > ImageType; typedef itk::Image< double, VImageDimension > DoubleImageType; typedef itk::UnaryFunctorImageFilter< DoubleImageType, ImageType, mitk::Functor::RoundValue > DefaultFilterType; typename DoubleImageType::Pointer itkImage = DoubleImageType::New(); mitk::CastToItkImage(resampledImage, itkImage); typename DefaultFilterType::Pointer filter = DefaultFilterType::New(); filter->SetInput(itkImage); filter->Update(); CastToMitkImage(filter->GetOutput(), outputImage); } mitk::Image::Pointer mitk::TransformationOperation::ResampleImage(Image::Pointer &image, mitk::Vector3D spacingVector, mitk::ImageMappingInterpolator::Type interpolator, GridInterpolationPositionType position, bool returnAsDouble, bool roundOutput) { // Convert image to double if required mitk::Image::Pointer tmpImage = image; if (returnAsDouble) { AccessByItk_n(image, ExecuteImageTypeToDouble, (tmpImage)); } auto newGeometry = image->GetGeometry()->Clone(); mitk::Vector3D spacing; mitk::BaseGeometry::BoundsArrayType bounds = newGeometry->GetBounds(); for (int i = 0; i < 3; ++i) { spacing[i] = newGeometry->GetSpacing()[i]; //bounds[i*2+1] = newGeometry->GetBounds()[i * 2 + 1]; if (spacingVector[i] > 0) { spacing[i] = spacingVector[i]; if (position == mitk::GridInterpolationPositionType::SameSize) { unsigned int samples = image->GetDimensions()[i]; double currentSpacing = newGeometry->GetSpacing()[i]; double newFactor = std::floor(samples*currentSpacing / spacingVector[i]); spacing[i] = samples * currentSpacing / newFactor; } } bounds[i * 2] = 0; bounds[i*2+1] = std::ceil(bounds[i*2+1] * newGeometry->GetSpacing()[i] *1.0 / spacing[i]); } mitk::Point3D origin = newGeometry->GetOrigin(); if (position == mitk::GridInterpolationPositionType::CenterAligned) { for (int i = 0; i < 3; ++i) { double oldLength = newGeometry->GetSpacing()[i] * newGeometry->GetBounds()[i*2+1]; double newLength = spacing[i] * bounds[i*2+1]; origin[i] = origin[i] - (newLength - oldLength) / 2; } } newGeometry->SetSpacing(spacing); newGeometry->SetOrigin(origin); newGeometry->SetBounds(bounds); mitk::Image::Pointer tmpResult = ImageMappingHelper::map( tmpImage, mitk::GenerateIdentityRegistration3D().GetPointer(), false, 0.0, //Padding Value newGeometry.GetPointer(), false, 0, //Error Value interpolator ); mitk::Image::Pointer result = mitk::Image::New(); if (roundOutput) { AccessByItk_n(tmpImage, ExecuteRoundImage, (tmpResult, result)); } else { result = tmpResult; } return result; } template static void ExecuteImageThresholding(itk::Image* image, mitk::Image::Pointer &resultImage) { typedef itk::Image ImageType; typedef itk::Image MaskType; typedef itk::UnaryFunctorImageFilter< ImageType, MaskType, mitk::Functor::ThresholdValue > DefaultFilterType; typename DefaultFilterType::Pointer filter = DefaultFilterType::New(); filter->SetInput(image); filter->GetFunctor().value = 0.5; filter->Update(); CastToMitkImage(filter->GetOutput(), resultImage); } mitk::Image::Pointer mitk::TransformationOperation::ResampleMask(Image::Pointer &image, mitk::Vector3D spacingVector, mitk::ImageMappingInterpolator::Type interpolator, GridInterpolationPositionType position) { mitk::Image::Pointer result; if (interpolator == mitk::ImageMappingInterpolator::NearestNeighbor) { result = TransformationOperation::ResampleImage(image, spacingVector, interpolator, position, false, false); } else { auto tmpResult = TransformationOperation::ResampleImage(image, spacingVector, interpolator, position, true, false); AccessByItk_n(tmpResult, ExecuteImageThresholding, (result)); } return result; } namespace itk { namespace utils { IndexPairType IndexToLevelBandSteerablePyramid(unsigned int linearIndex, unsigned int levels, unsigned int bands) { unsigned int totalOutputs = 1 + levels * bands; if (linearIndex > totalOutputs - 1) { itkGenericExceptionMacro(<< "Failed converting linearIndex " << linearIndex << " with levels: " << levels << " bands: " << bands << " to Level,Band pair : out of bounds"); } // Low pass (band = 0). if (linearIndex == totalOutputs - 1) { return std::make_pair(levels - 1, 0); } unsigned int band = (linearIndex) % bands + 1; // note integer division ahead. unsigned int level = (linearIndex) / bands; itkAssertInDebugAndIgnoreInReleaseMacro(level < levels); return std::make_pair(level, band); } // Instantiation template unsigned int ComputeMaxNumberOfLevels<3>(const Size< 3 >& inputSize, const unsigned int & scaleFactor); template unsigned int ComputeMaxNumberOfLevels<2>(const Size< 2 >& inputSize, const unsigned int & scaleFactor); } // end namespace utils } // end namespace itk diff --git a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp index 9228f7f78a..d092bb6d5d 100644 --- a/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp +++ b/Modules/BoundingShape/src/DataManagement/mitkBoundingShapeCropper.cpp @@ -1,338 +1,346 @@ /*============================================================================ 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 "mitkBoundingShapeCropper.h" #include "mitkGeometry3D.h" #include "mitkImageAccessByItk.h" #include "mitkImageCast.h" #include "mitkImageToItk.h" #include "mitkStatusBar.h" #include "mitkTimeHelper.h" #include #include "vtkMatrix4x4.h" #include "vtkSmartPointer.h" #include "vtkTransform.h" #include "itkImageRegionIteratorWithIndex.h" #include #include #include #include namespace mitk { BoundingShapeCropper::BoundingShapeCropper() : m_Geometry(nullptr), m_OutsideValue(0), m_UseCropTimeStepOnly(false), m_CurrentTimeStep(0), m_UseWholeInputRegion(false), m_InputTimeSelector(mitk::ImageTimeSelector::New()), m_OutputTimeSelector(mitk::ImageTimeSelector::New()) { this->SetNumberOfIndexedInputs(2); this->SetNumberOfRequiredInputs(2); } BoundingShapeCropper::~BoundingShapeCropper() {} template void BoundingShapeCropper::CutImage(itk::Image *inputItkImage, int timeStep) { MITK_INFO << "Scalar Pixeltype" << std::endl; typedef TPixel TOutputPixel; typedef itk::Image ItkInputImageType; typedef itk::Image ItkOutputImageType; typedef typename itk::ImageBase::RegionType ItkRegionType; typedef itk::ImageRegionIteratorWithIndex ItkInputImageIteratorType; typedef itk::ImageRegionIteratorWithIndex ItkOutputImageIteratorType; TOutputPixel outsideValue = this->GetOutsideValue(); // currently 0 if not set in advance // TODO: change default value to itk::NumericTraits::min(); if (this->m_Geometry.IsNull()) return; if (inputItkImage == nullptr) { mitk::StatusBar::GetInstance()->DisplayErrorText( "An internal error occurred. Can't convert Image. Please report to bugs@mitk.org"); std::cout << " image is nullptr...returning" << std::endl; return; } // first convert the index typename ItkRegionType::IndexType::IndexValueType tmpIndex[3]; itk2vtk(this->m_InputRequestedRegion.GetIndex(), tmpIndex); typename ItkRegionType::IndexType index; index.SetIndex(tmpIndex); // then convert the size typename ItkRegionType::SizeType::SizeValueType tmpSize[3]; itk2vtk(this->m_InputRequestedRegion.GetSize(), tmpSize); typename ItkRegionType::SizeType size; size.SetSize(tmpSize); // create the ITK-image-region out of index and size ItkRegionType inputRegionOfInterest(index, size); // Get access to the MITK output image via an ITK image typename mitk::ImageToItk::Pointer outputimagetoitk = mitk::ImageToItk::New(); outputimagetoitk->SetInput(this->m_OutputTimeSelector->GetOutput()); outputimagetoitk->Update(); typename ItkOutputImageType::Pointer outputItkImage = outputimagetoitk->GetOutput(); // create the iterators ItkInputImageIteratorType inputIt(inputItkImage, inputRegionOfInterest); ItkOutputImageIteratorType outputIt(outputItkImage, outputItkImage->GetLargestPossibleRegion()); // Cut the boundingbox out of the image by iterating through all images // TODO: use more efficient method by using the contour instead off all single pixels mitk::Point3D p; mitk::BaseGeometry *inputGeometry = this->GetInput()->GetGeometry(timeStep); // calculates translation based on offset+extent not on the transformation matrix // NOTE: center of the box is vtkSmartPointer imageTransform = this->m_Geometry->GetGeometry()->GetVtkTransform()->GetMatrix(); Point3D center = this->m_Geometry->GetGeometry()->GetCenter(); auto translation = vtkSmartPointer::New(); translation->Translate(center[0] - imageTransform->GetElement(0, 3), center[1] - imageTransform->GetElement(1, 3), center[2] - imageTransform->GetElement(2, 3)); auto transform = vtkSmartPointer::New(); transform->SetMatrix(imageTransform); transform->PostMultiply(); transform->Concatenate(translation); transform->Update(); mitk::Vector3D extent; for (unsigned int i = 0; i < 3; ++i) extent[i] = (this->m_Geometry->GetGeometry()->GetExtent(i)); for (inputIt.GoToBegin(), outputIt.GoToBegin(); !inputIt.IsAtEnd(); ++inputIt, ++outputIt) { vtk2itk(inputIt.GetIndex(), p); inputGeometry->IndexToWorld(p, p); ScalarType p2[4]; p2[0] = p[0]; p2[1] = p[1]; p2[2] = p[2]; p2[3] = 1; // transform point from world to object coordinates transform->GetInverse()->TransformPoint(p2, p2); // check if the world point is within bounds bool isInside = (p2[0] >= (-extent[0] / 2.0)) && (p2[0] <= (extent[0] / 2.0)) && (p2[1] >= (-extent[1] / 2.0)) && (p2[1] <= (extent[1] / 2.0)) && (p2[2] >= (-extent[2] / 2.0)) && (p2[2] <= (extent[2] / 2.0)); if ((!this->m_UseCropTimeStepOnly && isInside) || (this->m_UseCropTimeStepOnly && timeStep == this->m_CurrentTimeStep && isInside)) { outputIt.Set((TOutputPixel)inputIt.Value()); } else { outputIt.Set(outsideValue); } } } void BoundingShapeCropper::SetGeometry(const mitk::GeometryData *geometry) { m_Geometry = const_cast(geometry); // Process object is not const-correct so the const_cast is required here this->ProcessObject::SetNthInput(1, const_cast(geometry)); } // const mitk::GeometryData* BoundingShapeCropper::GetGeometryData() const //{ // return m_Geometry.GetPointer(); //} const mitk::PixelType BoundingShapeCropper::GetOutputPixelType() { return this->GetInput()->GetPixelType(); } void BoundingShapeCropper::GenerateInputRequestedRegion() { mitk::Image *output = this->GetOutput(); if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) || (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0)) return; GenerateTimeInInputRegion(output, this->GetInput()); } void BoundingShapeCropper::GenerateOutputInformation() { // Set Cropping region mitk::Image::Pointer output = this->GetOutput(); if ((output->IsInitialized()) && (output->GetPipelineMTime() <= m_TimeOfHeaderInitialization.GetMTime())) return; mitk::Image::Pointer input = this->GetInput(); if (input.IsNull()) { mitkThrow() << "Input is not a mitk::Image"; } itkDebugMacro(<< "GenerateOutputInformation()"); unsigned int dimension = input->GetDimension(); if (dimension < 3) { mitkThrow() << "ImageCropper cannot handle 1D or 2D Objects."; } if ((m_Geometry.IsNull()) || (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0)) return; mitk::BaseGeometry *bsGeometry = m_Geometry->GetGeometry(); mitk::BaseGeometry *inputImageGeometry = input->GetSlicedGeometry(); // calculate bounding box mitk::BoundingBox::Pointer bsBoxRelativeToImage = bsGeometry->CalculateBoundingBoxRelativeToTransform(inputImageGeometry->GetIndexToWorldTransform()); // pre-initialize input-requested-region to largest-possible-region m_InputRequestedRegion = input->GetLargestPossibleRegion(); // build region out of bounding-box of index and size of the bounding box mitk::SlicedData::IndexType index = m_InputRequestedRegion.GetIndex(); // init times and channels mitk::BoundingBox::PointType min = bsBoxRelativeToImage->GetMinimum(); mitk::SlicedData::SizeType size = m_InputRequestedRegion.GetSize(); // init times and channels mitk::BoundingBox::PointType max = bsBoxRelativeToImage->GetMaximum(); mitk::Point maxCorrected; - maxCorrected[0] = max[0]; - maxCorrected[1] = max[1]; - maxCorrected[2] = max[2]; + mitk::Point minCorrected; + + for (unsigned int i = 0; i < 3; i++) + { + maxCorrected[i] = max[i]; + minCorrected[i] = min[i]; + } + maxCorrected[3] = input->GetDimensions()[3]; maxCorrected[4] = 0; + minCorrected[3] = 0; + minCorrected[4] = 0; for (unsigned int i = 0; i < dimension; i++) { - index[i] = (mitk::SlicedData::IndexType::IndexValueType)(std::ceil(min[i])); + index[i] = (mitk::SlicedData::IndexType::IndexValueType)(std::ceil(minCorrected[i])); size[i] = (mitk::SlicedData::SizeType::SizeValueType)(std::ceil(maxCorrected[i]) - index[i]); } + mitk::SlicedData::RegionType bsRegion(index, size); if (m_UseWholeInputRegion == false) { // crop input-requested-region with region of bounding-object if (m_InputRequestedRegion.Crop(bsRegion) == false) { // crop not possible => do nothing: set time size to 0. size.Fill(0); m_InputRequestedRegion.SetSize(size); bsRegion.SetSize(size); mitkThrow() << "No overlap of the image and the cropping object."; } } // initialize output image auto dimensions = new unsigned int[dimension]; if (dimension > 3 && !this->GetUseCropTimeStepOnly()) memcpy(dimensions + 3, input->GetDimensions() + 3, (dimension - 3) * sizeof(unsigned int)); else dimension = 3; // set timeStep to zero if GetUseCropTimeStepOnly is true itk2vtk(m_InputRequestedRegion.GetSize(), dimensions); output->Initialize(mitk::PixelType(GetOutputPixelType()), dimension, dimensions); delete[] dimensions; // Apply transform of the input image to the new generated output image mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion(); m_TimeOfHeaderInitialization.Modified(); } void BoundingShapeCropper::ComputeData(mitk::Image *image, int boTimeStep) { // examine dimension and pixeltype if ((image == nullptr) || (image->GetDimension() > 4) || (image->GetDimension() <= 2)) { MITK_ERROR << "Filter cannot handle dimensions less than 2 and greater than 4" << std::endl; itkExceptionMacro("Filter cannot handle dimensions less than 2 and greater than 4"); return; } AccessByItk_1(image, CutImage, boTimeStep); } void BoundingShapeCropper::GenerateData() { MITK_INFO << "Generate Data" << std::endl; mitk::Image::ConstPointer input = this->GetInput(); mitk::Image::Pointer output = this->GetOutput(); if (input.IsNull()) return; if ((output->IsInitialized() == false) || (m_Geometry.IsNull()) || (m_Geometry->GetTimeGeometry()->CountTimeSteps() == 0)) return; m_InputTimeSelector->SetInput(input); m_OutputTimeSelector->SetInput(this->GetOutput()); mitk::BoundingShapeCropper::RegionType outputRegion = output->GetRequestedRegion(); mitk::BaseGeometry *inputImageGeometry = input->GetSlicedGeometry(); // iterate over all time steps and perform cropping or masking on all or a specific timestep (perviously specified // by UseCurrentTimeStepOnly) int tstart = outputRegion.GetIndex(3); int tmax = tstart + outputRegion.GetSize(3); if (this->m_UseCropTimeStepOnly) { mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(tstart); auto indexToWorldTransform = AffineTransform3D::New(); indexToWorldTransform->SetParameters(input->GetSlicedGeometry(tstart)->GetIndexToWorldTransform()->GetParameters()); slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform); const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex(); mitk::Point3D origin; vtk2itk(start, origin); inputImageGeometry->IndexToWorld(origin, origin); slicedGeometry->SetOrigin(origin); m_InputTimeSelector->SetTimeNr(m_CurrentTimeStep); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(tstart); m_OutputTimeSelector->UpdateLargestPossibleRegion(); ComputeData(m_InputTimeSelector->GetOutput(), m_CurrentTimeStep); } else { int t; for (t = tstart; t < tmax; ++t) { mitk::SlicedGeometry3D *slicedGeometry = output->GetSlicedGeometry(t); auto indexToWorldTransform = AffineTransform3D::New(); indexToWorldTransform->SetParameters(input->GetSlicedGeometry(t)->GetIndexToWorldTransform()->GetParameters()); slicedGeometry->SetIndexToWorldTransform(indexToWorldTransform); const mitk::SlicedData::IndexType &start = m_InputRequestedRegion.GetIndex(); mitk::Point3D origin; vtk2itk(start, origin); inputImageGeometry->IndexToWorld(origin, origin); slicedGeometry->SetOrigin(origin); m_InputTimeSelector->SetTimeNr(t); m_InputTimeSelector->UpdateLargestPossibleRegion(); m_OutputTimeSelector->SetTimeNr(t); m_OutputTimeSelector->UpdateLargestPossibleRegion(); ComputeData(m_InputTimeSelector->GetOutput(), t); } } m_InputTimeSelector->SetInput(nullptr); m_OutputTimeSelector->SetInput(nullptr); m_TimeOfHeaderInitialization.Modified(); } } // of namespace mitk diff --git a/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp b/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp index 2cab1b9183..2e29e11d93 100644 --- a/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp +++ b/Modules/Classification/CLMiniApps/CLMatchPointReg.cpp @@ -1,164 +1,164 @@ /*============================================================================ 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 "mitkProperties.h" #include "mitkCommandLineParser.h" #include "mitkIOUtil.h" #include "mitkPreferenceListReaderOptionsFunctor.h" // MatchPoint #include #include #include #include #include #include #include #include #include #include #include -#include +#include // Qt #include #include #include //#include #include #include #include #include int main(int argc, char* argv[]) { mitkCommandLineParser parser; parser.setTitle("Dicom Loader"); parser.setCategory("Preprocessing Tools"); parser.setDescription(""); parser.setContributor("German Cancer Research Center (DKFZ)"); parser.setArgumentPrefix("--","-"); // Add command line argument names parser.addArgument("help", "h",mitkCommandLineParser::Bool, "Help:", "Show this help text"); parser.addArgument("moving", "m", mitkCommandLineParser::Directory, "Input folder:", "Input folder", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("fixed", "f", mitkCommandLineParser::Directory, "Input folder:", "Input folder", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file:", "Output file", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.addArgument("reader", "r", mitkCommandLineParser::Int, "Reader ID", "Reader Name", us::Any(), false); parser.addArgument("interpolation", "interp", mitkCommandLineParser::Int, "Reader ID", "Reader Name", us::Any(), false); std::map parsedArgs = parser.parseArguments(argc, argv); QFileInfo fi(argv[0]); map::deployment::DLLDirectoryBrowser::Pointer browser = map::deployment::DLLDirectoryBrowser::New(); browser->addDLLSearchLocation(QDir::homePath().toStdString()); browser->addDLLSearchLocation(QDir::currentPath().toStdString()); browser->addDLLSearchLocation(fi.canonicalPath().toStdString()); browser->update(); auto dllList = browser->getLibraryInfos(); int id = 0; std::cout << std::endl << " --- Algorithm List --- " << std::endl; for (auto info : dllList) { std::cout << "Algorithm ID " << id << ": " << info->getAlgorithmUID().getName() << std::endl; ++id; } std::cout << std::endl << " --- Interpolation List --- " << std::endl; std::cout << "Interpolation ID 0: Linear Interpolation " << std::endl; std::cout << "Interpolation ID 1: Nearest Neighbour" << std::endl; std::cout << "Interpolation ID 2: BSpline 3D" << std::endl << std::endl; mitk::ImageMappingInterpolator::Type interpolationMode = mitk::ImageMappingInterpolator::Linear; if (parsedArgs.size()==0) return EXIT_FAILURE; // Show a help message if ( parsedArgs.count("help") || parsedArgs.count("h")) { std::cout << parser.helpText(); return EXIT_SUCCESS; } std::string movingFile = us::any_cast(parsedArgs["moving"]); std::string fixedFile = us::any_cast(parsedArgs["fixed"]); int selectedAlgorithm = us::any_cast(parsedArgs["reader"]); std::string outputPath = us::any_cast(parsedArgs["output"]); if (parsedArgs.count("interpolation")) { switch (us::any_cast(parsedArgs["interpolation"])) { case 0: interpolationMode = mitk::ImageMappingInterpolator::Linear; break; case 1: interpolationMode = mitk::ImageMappingInterpolator::NearestNeighbor; break; case 2: interpolationMode = mitk::ImageMappingInterpolator::BSpline_3; break; default: interpolationMode = mitk::ImageMappingInterpolator::Linear; } } mitk::Image::Pointer movingImage = mitk::IOUtil::Load(movingFile); mitk::Image::Pointer fixedImage = mitk::IOUtil::Load(fixedFile); auto dllInfo = dllList[selectedAlgorithm]; if (!dllInfo) { MITK_ERROR << "No valid algorithm is selected. Cannot load algorithm. ABORTING."; return -1; } ::map::deployment::DLLHandle::Pointer tempDLLHandle = ::map::deployment::openDeploymentDLL( dllInfo->getLibraryFilePath()); ::map::algorithm::RegistrationAlgorithmBase::Pointer tempAlgorithm = ::map::deployment::getRegistrationAlgorithm(tempDLLHandle); MITK_INFO << "Well...."; if (tempAlgorithm.IsNull()) { MITK_ERROR << "Error. Cannot load selected algorithm."; return -2; } - mitk::MITKAlgorithmHelper helper(tempAlgorithm); + mitk::MAPAlgorithmHelper helper(tempAlgorithm); helper.SetData(movingImage, fixedImage); auto registration = helper.GetRegistration(); MITK_INFO << "Well...."; mitk::Image::Pointer spResultData= mitk::ImageMappingHelper::map(movingImage, registration, false, // Use all Pixels 0.0, // Padding Value fixedImage->GetGeometry()->Clone().GetPointer(), // Ref. Geometry false, //!(this->m_allowUnregPixels), 0, // Error Value interpolationMode // Interpolator Type ); MITK_INFO << "Well...."; mitk::IOUtil::Save(spResultData, outputPath); return EXIT_SUCCESS; } diff --git a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp index ac0d4f036e..db6186d3e3 100644 --- a/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp +++ b/Modules/Core/TestingHelper/src/mitkRenderingTestHelper.cpp @@ -1,292 +1,292 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // VTK #include #include #include #include #include #include // MITK #include #include #include #include #include #include #include #include #if defined _MSC_VER #if _MSC_VER >= 1700 #define RESIZE_WORKAROUND #endif #endif #ifdef RESIZE_WORKAROUND #include "vtkWin32OpenGLRenderWindow.h" #endif // VTK Testing to compare the rendered image pixel-wise against a reference screen shot #include "vtkTesting.h" mitk::RenderingTestHelper::RenderingTestHelper(int width, int height, AntiAliasing antiAliasing) : m_AutomaticallyCloseRenderWindow(true) { this->Initialize(width, height, antiAliasing); } mitk::RenderingTestHelper::RenderingTestHelper( int width, int height, int argc, char *argv[], AntiAliasing antiAliasing) : m_AutomaticallyCloseRenderWindow(true) { this->Initialize(width, height, antiAliasing); this->SetInputFileNames(argc, argv); } void mitk::RenderingTestHelper::Initialize(int width, int height, AntiAliasing antiAliasing) { RenderingManager::GetInstance()->SetAntiAliasing(antiAliasing); - mitk::UIDGenerator uidGen = mitk::UIDGenerator("UnnamedRenderer_", 8); + mitk::UIDGenerator uidGen = mitk::UIDGenerator("UnnamedRenderer_"); m_RenderWindow = mitk::RenderWindow::New(nullptr, uidGen.GetUID().c_str()); auto renderWindow = m_RenderWindow->GetVtkRenderWindow(); if (0 == renderWindow->SupportsOpenGL()) { auto openGLRenderWindow = dynamic_cast(renderWindow); auto message = nullptr != openGLRenderWindow ? openGLRenderWindow->GetOpenGLSupportMessage() : std::string("No details available."); mitkThrowException(mitk::TestNotRunException) << "OpenGL not supported: " << message; } m_DataStorage = mitk::StandaloneDataStorage::New(); m_RenderWindow->GetRenderer()->SetDataStorage(m_DataStorage); this->SetMapperIDToRender2D(); this->GetVtkRenderWindow()->SetSize(width, height); #ifdef RESIZE_WORKAROUND HWND hWnd = static_cast(this->GetVtkRenderWindow())->GetWindowId(); RECT r; r.left = 10; r.top = 10; r.right = r.left + width; r.bottom = r.top + height; LONG style = GetWindowLong(hWnd, GWL_STYLE); AdjustWindowRect(&r, style, FALSE); MITK_INFO << "WANTED:"; MITK_INFO << r.right - r.left; MITK_INFO << r.bottom - r.top; RECT rect; if (GetWindowRect(hWnd, &rect)) { int width = rect.right - rect.left; int height = rect.bottom - rect.top; MITK_INFO << "ACTUAL:"; MITK_INFO << width; MITK_INFO << height; } SetWindowPos(hWnd, HWND_TOP, 0, 0, r.right - r.left, r.bottom - r.top, SWP_NOZORDER); GetWindowRect(hWnd, &rect); int width2 = rect.right - rect.left; int height2 = rect.bottom - rect.top; MITK_INFO << "ACTUAL2:"; MITK_INFO << width2; MITK_INFO << height2; SetWindowPos(hWnd, HWND_TOP, 0, 0, 2 * (r.right - r.left) - width2, 2 * (r.bottom - r.top) - height2, SWP_NOZORDER); #endif m_RenderWindow->GetRenderer()->Resize(width, height); } mitk::RenderingTestHelper::~RenderingTestHelper() { } void mitk::RenderingTestHelper::SetMapperID(mitk::BaseRenderer::StandardMapperSlot id) { m_RenderWindow->GetRenderer()->SetMapperID(id); } void mitk::RenderingTestHelper::SetMapperIDToRender3D() { this->SetMapperID(mitk::BaseRenderer::Standard3D); mitk::RenderingManager::GetInstance()->InitializeViews( this->GetDataStorage()->ComputeBoundingGeometry3D(this->GetDataStorage()->GetAll())); } void mitk::RenderingTestHelper::SetMapperIDToRender2D() { this->SetMapperID(mitk::BaseRenderer::Standard2D); } void mitk::RenderingTestHelper::Render() { // if the datastorage is initialized and at least 1 image is loaded render it if (m_DataStorage.IsNotNull() || m_DataStorage->GetAll()->Size() >= 1) { // Prepare the VTK camera before rendering. m_RenderWindow->GetRenderer()->PrepareRender(); this->GetVtkRenderWindow()->Render(); this->GetVtkRenderWindow()->WaitForCompletion(); if (m_AutomaticallyCloseRenderWindow == false) { // Use interaction to stop the test this->GetVtkRenderWindow()->GetInteractor()->Start(); } } else { MITK_ERROR << "No images loaded in data storage!"; } } mitk::DataStorage::Pointer mitk::RenderingTestHelper::GetDataStorage() { return m_DataStorage; } void mitk::RenderingTestHelper::SetInputFileNames(int argc, char *argv[]) { // i is set 1, because 0 is the testname as string // parse parameters for (int i = 1; i < argc; ++i) { // add everything to a list but -T and -V std::string tmp = argv[i]; if ((tmp.compare("-T")) && (tmp.compare("-V"))) { this->AddToStorage(tmp); } else { break; } } } void mitk::RenderingTestHelper::SetViewDirection(mitk::SliceNavigationController::ViewDirection viewDirection) { mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow()) ->GetSliceNavigationController() ->SetDefaultViewDirection(viewDirection); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } void mitk::RenderingTestHelper::ReorientSlices(mitk::Point3D origin, mitk::Vector3D rotation) { mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::BaseRenderer::GetInstance(m_RenderWindow->GetVtkRenderWindow())->GetSliceNavigationController(); sliceNavigationController->ReorientSlices(origin, rotation); } vtkRenderer *mitk::RenderingTestHelper::GetVtkRenderer() { return m_RenderWindow->GetRenderer()->GetVtkRenderer(); } void mitk::RenderingTestHelper::SetImageProperty(const char *propertyKey, mitk::BaseProperty *property) { this->m_DataStorage->GetNode(mitk::NodePredicateDataType::New("Image"))->SetProperty(propertyKey, property); } vtkRenderWindow *mitk::RenderingTestHelper::GetVtkRenderWindow() { return m_RenderWindow->GetVtkRenderWindow(); } bool mitk::RenderingTestHelper::CompareRenderWindowAgainstReference(int argc, char *argv[], double threshold) { this->Render(); // retVal meanings: (see VTK/Rendering/vtkTesting.h) // 0 = test failed // 1 = test passed // 2 = test not run // 3 = something with vtkInteraction if (vtkTesting::Test(argc, argv, this->GetVtkRenderWindow(), threshold) == 1) return true; else return false; } // method to save a screenshot of the renderwindow (e.g. create a reference screenshot) void mitk::RenderingTestHelper::SaveAsPNG(std::string fileName) { vtkSmartPointer renderer = this->GetVtkRenderer(); bool doubleBuffering(renderer->GetRenderWindow()->GetDoubleBuffer()); renderer->GetRenderWindow()->DoubleBufferOff(); vtkSmartPointer magnifier = vtkSmartPointer::New(); magnifier->SetInput(renderer); magnifier->SetMagnification(1); vtkSmartPointer fileWriter = vtkSmartPointer::New(); fileWriter->SetInputConnection(magnifier->GetOutputPort()); fileWriter->SetFileName(fileName.c_str()); fileWriter->Write(); renderer->GetRenderWindow()->SetDoubleBuffer(doubleBuffering); } void mitk::RenderingTestHelper::SetAutomaticallyCloseRenderWindow(bool automaticallyCloseRenderWindow) { m_AutomaticallyCloseRenderWindow = automaticallyCloseRenderWindow; } void mitk::RenderingTestHelper::SaveReferenceScreenShot(std::string fileName) { this->SaveAsPNG(fileName); } void mitk::RenderingTestHelper::AddToStorage(const std::string &filename) { try { mitk::IOUtil::Load(filename, *m_DataStorage.GetPointer()); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } catch ( const itk::ExceptionObject &e ) { MITK_ERROR << "Failed loading test data '" << filename << "': " << e.what(); } } void mitk::RenderingTestHelper::AddNodeToStorage(mitk::DataNode::Pointer node) { this->m_DataStorage->Add(node); mitk::RenderingManager::GetInstance()->InitializeViews( m_DataStorage->ComputeBoundingGeometry3D(m_DataStorage->GetAll())); } diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index 75407c5ac8..3178dddf16 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,325 +1,326 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkCoreActivator.cpp mitkCoreObjectFactoryBase.cpp mitkCoreObjectFactory.cpp mitkCoreServices.cpp mitkException.cpp Algorithms/mitkBaseDataSource.cpp Algorithms/mitkClippedSurfaceBoundsCalculator.cpp Algorithms/mitkCompareImageDataFilter.cpp Algorithms/mitkCompositePixelValueToString.cpp Algorithms/mitkConvert2Dto3DImageFilter.cpp Algorithms/mitkDataNodeSource.cpp Algorithms/mitkExtractSliceFilter.cpp Algorithms/mitkExtractSliceFilter2.cpp Algorithms/mitkHistogramGenerator.cpp Algorithms/mitkImageChannelSelector.cpp Algorithms/mitkImageSliceSelector.cpp Algorithms/mitkImageSource.cpp Algorithms/mitkImageTimeSelector.cpp Algorithms/mitkImageToImageFilter.cpp Algorithms/mitkImageToSurfaceFilter.cpp Algorithms/mitkMultiComponentImageDataComparisonFilter.cpp Algorithms/mitkPlaneGeometryDataToSurfaceFilter.cpp Algorithms/mitkPointSetSource.cpp Algorithms/mitkPointSetToPointSetFilter.cpp Algorithms/mitkRGBToRGBACastImageFilter.cpp Algorithms/mitkSubImageSelector.cpp Algorithms/mitkSurfaceSource.cpp Algorithms/mitkSurfaceToImageFilter.cpp Algorithms/mitkSurfaceToSurfaceFilter.cpp Algorithms/mitkUIDGenerator.cpp Algorithms/mitkVolumeCalculator.cpp Algorithms/mitkTemporalJoinImagesFilter.cpp Controllers/mitkBaseController.cpp Controllers/mitkCallbackFromGUIThread.cpp Controllers/mitkCameraController.cpp Controllers/mitkCameraRotationController.cpp Controllers/mitkLimitedLinearUndo.cpp Controllers/mitkOperationEvent.cpp Controllers/mitkPlanePositionManager.cpp Controllers/mitkProgressBar.cpp Controllers/mitkRenderingManager.cpp Controllers/mitkSliceNavigationController.cpp Controllers/mitkSlicesCoordinator.cpp Controllers/mitkStatusBar.cpp Controllers/mitkStepper.cpp Controllers/mitkTestManager.cpp Controllers/mitkUndoController.cpp Controllers/mitkVerboseLimitedLinearUndo.cpp Controllers/mitkVtkLayerController.cpp DataManagement/mitkAnatomicalStructureColorPresets.cpp DataManagement/mitkArbitraryTimeGeometry.cpp DataManagement/mitkAbstractTransformGeometry.cpp DataManagement/mitkAnnotationProperty.cpp DataManagement/mitkApplicationCursor.cpp DataManagement/mitkApplyTransformMatrixOperation.cpp DataManagement/mitkBaseData.cpp DataManagement/mitkBaseGeometry.cpp DataManagement/mitkBaseProperty.cpp DataManagement/mitkChannelDescriptor.cpp DataManagement/mitkClippingProperty.cpp DataManagement/mitkColorProperty.cpp DataManagement/mitkDataNode.cpp DataManagement/mitkDataStorage.cpp DataManagement/mitkEnumerationProperty.cpp DataManagement/mitkFloatPropertyExtension.cpp DataManagement/mitkGeometry3D.cpp DataManagement/mitkGeometryData.cpp DataManagement/mitkGeometryTransformHolder.cpp DataManagement/mitkGroupTagProperty.cpp DataManagement/mitkGenericIDRelationRule.cpp DataManagement/mitkIdentifiable.cpp DataManagement/mitkImageAccessorBase.cpp DataManagement/mitkImageCaster.cpp DataManagement/mitkImageCastPart1.cpp DataManagement/mitkImageCastPart2.cpp DataManagement/mitkImageCastPart3.cpp DataManagement/mitkImageCastPart4.cpp DataManagement/mitkImage.cpp DataManagement/mitkImageDataItem.cpp DataManagement/mitkImageDescriptor.cpp DataManagement/mitkImageReadAccessor.cpp DataManagement/mitkImageStatisticsHolder.cpp DataManagement/mitkImageVtkAccessor.cpp DataManagement/mitkImageVtkReadAccessor.cpp DataManagement/mitkImageVtkWriteAccessor.cpp DataManagement/mitkImageWriteAccessor.cpp DataManagement/mitkIntPropertyExtension.cpp DataManagement/mitkIPersistenceService.cpp DataManagement/mitkIPropertyAliases.cpp DataManagement/mitkIPropertyDescriptions.cpp DataManagement/mitkIPropertyExtensions.cpp DataManagement/mitkIPropertyFilters.cpp DataManagement/mitkIPropertyOwner.cpp DataManagement/mitkIPropertyPersistence.cpp DataManagement/mitkIPropertyProvider.cpp DataManagement/mitkLandmarkProjectorBasedCurvedGeometry.cpp DataManagement/mitkLandmarkProjector.cpp DataManagement/mitkLevelWindow.cpp DataManagement/mitkLevelWindowManager.cpp DataManagement/mitkLevelWindowPreset.cpp DataManagement/mitkLevelWindowProperty.cpp DataManagement/mitkLine.cpp DataManagement/mitkLookupTable.cpp DataManagement/mitkLookupTableProperty.cpp DataManagement/mitkLookupTables.cpp # specializations of GenericLookupTable DataManagement/mitkMaterial.cpp DataManagement/mitkMemoryUtilities.cpp DataManagement/mitkModalityProperty.cpp DataManagement/mitkModifiedLock.cpp DataManagement/mitkNodePredicateAnd.cpp DataManagement/mitkNodePredicateBase.cpp DataManagement/mitkNodePredicateCompositeBase.cpp DataManagement/mitkNodePredicateData.cpp DataManagement/mitkNodePredicateDataType.cpp DataManagement/mitkNodePredicateDataUID.cpp DataManagement/mitkNodePredicateDimension.cpp DataManagement/mitkNodePredicateFirstLevel.cpp DataManagement/mitkNodePredicateFunction.cpp DataManagement/mitkNodePredicateGeometry.cpp DataManagement/mitkNodePredicateNot.cpp DataManagement/mitkNodePredicateOr.cpp DataManagement/mitkNodePredicateProperty.cpp DataManagement/mitkNodePredicateDataProperty.cpp DataManagement/mitkNodePredicateSource.cpp + DataManagement/mitkNodePredicateSubGeometry.cpp DataManagement/mitkNumericConstants.cpp DataManagement/mitkPlaneGeometry.cpp DataManagement/mitkPlaneGeometryData.cpp DataManagement/mitkPlaneOperation.cpp DataManagement/mitkPlaneOrientationProperty.cpp DataManagement/mitkPointOperation.cpp DataManagement/mitkPointSet.cpp DataManagement/mitkPointSetShapeProperty.cpp DataManagement/mitkProperties.cpp DataManagement/mitkPropertyAliases.cpp DataManagement/mitkPropertyDescriptions.cpp DataManagement/mitkPropertyExtension.cpp DataManagement/mitkPropertyExtensions.cpp DataManagement/mitkPropertyFilter.cpp DataManagement/mitkPropertyFilters.cpp DataManagement/mitkPropertyKeyPath.cpp DataManagement/mitkPropertyList.cpp DataManagement/mitkPropertyListReplacedObserver.cpp DataManagement/mitkPropertyNameHelper.cpp DataManagement/mitkPropertyObserver.cpp DataManagement/mitkPropertyPersistence.cpp DataManagement/mitkPropertyPersistenceInfo.cpp DataManagement/mitkPropertyRelationRuleBase.cpp DataManagement/mitkProportionalTimeGeometry.cpp DataManagement/mitkRenderingModeProperty.cpp DataManagement/mitkResliceMethodProperty.cpp DataManagement/mitkRestorePlanePositionOperation.cpp DataManagement/mitkRotationOperation.cpp DataManagement/mitkScaleOperation.cpp DataManagement/mitkSlicedData.cpp DataManagement/mitkSlicedGeometry3D.cpp DataManagement/mitkSmartPointerProperty.cpp DataManagement/mitkStandaloneDataStorage.cpp DataManagement/mitkStringProperty.cpp DataManagement/mitkSurface.cpp DataManagement/mitkSurfaceOperation.cpp DataManagement/mitkSourceImageRelationRule.cpp DataManagement/mitkThinPlateSplineCurvedGeometry.cpp DataManagement/mitkTimeGeometry.cpp DataManagement/mitkTransferFunction.cpp DataManagement/mitkTransferFunctionInitializer.cpp DataManagement/mitkTransferFunctionProperty.cpp DataManagement/mitkTemporoSpatialStringProperty.cpp DataManagement/mitkUIDManipulator.cpp DataManagement/mitkVector.cpp DataManagement/mitkVectorProperty.cpp DataManagement/mitkVtkInterpolationProperty.cpp DataManagement/mitkVtkRepresentationProperty.cpp DataManagement/mitkVtkResliceInterpolationProperty.cpp DataManagement/mitkVtkScalarModeProperty.cpp DataManagement/mitkVtkVolumeRenderingProperty.cpp DataManagement/mitkWeakPointerProperty.cpp DataManagement/mitkIPropertyRelations.cpp DataManagement/mitkPropertyRelations.cpp Interactions/mitkAction.cpp Interactions/mitkBindDispatcherInteractor.cpp Interactions/mitkCrosshairPositionEvent.cpp Interactions/mitkDataInteractor.cpp Interactions/mitkDispatcher.cpp Interactions/mitkDisplayActionEventBroadcast.cpp Interactions/mitkDisplayActionEventFunctions.cpp Interactions/mitkDisplayActionEventHandler.cpp Interactions/mitkDisplayActionEventHandlerDesynchronized.cpp Interactions/mitkDisplayActionEventHandlerStd.cpp Interactions/mitkDisplayActionEventHandlerSynchronized.cpp Interactions/mitkDisplayCoordinateOperation.cpp Interactions/mitkDisplayInteractor.cpp Interactions/mitkEventConfig.cpp Interactions/mitkEventFactory.cpp Interactions/mitkEventRecorder.cpp Interactions/mitkEventStateMachine.cpp Interactions/mitkInteractionEventConst.cpp Interactions/mitkInteractionEvent.cpp Interactions/mitkInteractionEventHandler.cpp Interactions/mitkInteractionEventObserver.cpp Interactions/mitkInteractionKeyEvent.cpp Interactions/mitkInteractionPositionEvent.cpp Interactions/mitkInteractionSchemeSwitcher.cpp Interactions/mitkInternalEvent.cpp Interactions/mitkMouseDoubleClickEvent.cpp Interactions/mitkMouseModeSwitcher.cpp Interactions/mitkMouseMoveEvent.cpp Interactions/mitkMousePressEvent.cpp Interactions/mitkMouseReleaseEvent.cpp Interactions/mitkMouseWheelEvent.cpp Interactions/mitkPointSetDataInteractor.cpp Interactions/mitkSinglePointDataInteractor.cpp Interactions/mitkStateMachineAction.cpp Interactions/mitkStateMachineCondition.cpp Interactions/mitkStateMachineContainer.cpp Interactions/mitkStateMachineState.cpp Interactions/mitkStateMachineTransition.cpp Interactions/mitkVtkEventAdapter.cpp Interactions/mitkVtkInteractorStyle.cxx Interactions/mitkXML2EventParser.cpp IO/mitkAbstractFileIO.cpp IO/mitkAbstractFileReader.cpp IO/mitkAbstractFileWriter.cpp IO/mitkCustomMimeType.cpp IO/mitkFileReader.cpp IO/mitkFileReaderRegistry.cpp IO/mitkFileReaderSelector.cpp IO/mitkFileReaderWriterBase.cpp IO/mitkFileWriter.cpp IO/mitkFileWriterRegistry.cpp IO/mitkFileWriterSelector.cpp IO/mitkGeometry3DToXML.cpp IO/mitkIFileIO.cpp IO/mitkIFileReader.cpp IO/mitkIFileWriter.cpp IO/mitkGeometryDataReaderService.cpp IO/mitkGeometryDataWriterService.cpp IO/mitkImageGenerator.cpp IO/mitkImageVtkLegacyIO.cpp IO/mitkImageVtkXmlIO.cpp IO/mitkIMimeTypeProvider.cpp IO/mitkIOConstants.cpp IO/mitkIOMimeTypes.cpp IO/mitkIOUtil.cpp IO/mitkItkImageIO.cpp IO/mitkItkLoggingAdapter.cpp IO/mitkLegacyFileReaderService.cpp IO/mitkLegacyFileWriterService.cpp IO/mitkLocaleSwitch.cpp IO/mitkLog.cpp IO/mitkMimeType.cpp IO/mitkMimeTypeProvider.cpp IO/mitkOperation.cpp IO/mitkPixelType.cpp IO/mitkPointSetReaderService.cpp IO/mitkPointSetWriterService.cpp IO/mitkProportionalTimeGeometryToXML.cpp IO/mitkRawImageFileReader.cpp IO/mitkStandardFileLocations.cpp IO/mitkSurfaceStlIO.cpp IO/mitkSurfaceVtkIO.cpp IO/mitkSurfaceVtkLegacyIO.cpp IO/mitkSurfaceVtkXmlIO.cpp IO/mitkVtkLoggingAdapter.cpp IO/mitkPreferenceListReaderOptionsFunctor.cpp IO/mitkIOMetaInformationPropertyConstants.cpp Rendering/mitkAbstractAnnotationRenderer.cpp Rendering/mitkAnnotationUtils.cpp Rendering/mitkBaseRenderer.cpp #Rendering/mitkGLMapper.cpp Moved to deprecated LegacyGL Module Rendering/mitkGradientBackground.cpp Rendering/mitkImageVtkMapper2D.cpp Rendering/mitkMapper.cpp Rendering/mitkAnnotation.cpp Rendering/mitkPlaneGeometryDataMapper2D.cpp Rendering/mitkPlaneGeometryDataVtkMapper3D.cpp Rendering/mitkPointSetVtkMapper2D.cpp Rendering/mitkPointSetVtkMapper3D.cpp Rendering/mitkRenderWindowBase.cpp Rendering/mitkRenderWindow.cpp Rendering/mitkRenderWindowFrame.cpp #Rendering/mitkSurfaceGLMapper2D.cpp Moved to deprecated LegacyGL Module Rendering/mitkSurfaceVtkMapper2D.cpp Rendering/mitkSurfaceVtkMapper3D.cpp Rendering/mitkVtkEventProvider.cpp Rendering/mitkVtkMapper.cpp Rendering/mitkVtkPropRenderer.cpp Rendering/mitkVtkWidgetRendering.cpp Rendering/vtkMitkLevelWindowFilter.cpp Rendering/vtkMitkRectangleProp.cpp Rendering/vtkMitkRenderProp.cpp Rendering/vtkMitkThickSlicesFilter.cpp Rendering/vtkNeverTranslucentTexture.cpp ) set(RESOURCE_FILES Interactions/globalConfig.xml Interactions/DisplayInteraction.xml Interactions/DisplayConfig.xml Interactions/DisplayConfigPACS.xml Interactions/DisplayConfigPACSPan.xml Interactions/DisplayConfigPACSScroll.xml Interactions/DisplayConfigPACSZoom.xml Interactions/DisplayConfigPACSLevelWindow.xml Interactions/DisplayConfigMITK.xml Interactions/DisplayConfigMITKNoCrosshair.xml Interactions/DisplayConfigMITKRotation.xml Interactions/DisplayConfigMITKRotationUnCoupled.xml Interactions/DisplayConfigMITKSwivel.xml Interactions/DisplayConfigMITKLimited.xml Interactions/PointSet.xml Interactions/Legacy/StateMachine.xml Interactions/Legacy/DisplayConfigMITKTools.xml Interactions/PointSetConfig.xml mitkLevelWindowPresets.xml mitkAnatomicalStructureColorPresets.xml ) diff --git a/Modules/Core/include/mitkArbitraryTimeGeometry.h b/Modules/Core/include/mitkArbitraryTimeGeometry.h index d86baa7636..9b82658e8a 100644 --- a/Modules/Core/include/mitkArbitraryTimeGeometry.h +++ b/Modules/Core/include/mitkArbitraryTimeGeometry.h @@ -1,244 +1,252 @@ /*============================================================================ 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 ArbitraryTimeGeometry_h #define ArbitraryTimeGeometry_h //MITK #include #include #include namespace mitk { /** * \brief Organizes geometries over arbitrary defined time steps * * For this TimeGeometry implementation it is assumed that * the durations of the time steps are arbitrary and may differ. * The geometries of the time steps are independent, * and not linked to each other. Since the timeBounds of the * geometries are different for each time step it is not possible * to set the same geometry to different time steps. Instead * copies should be used. * @remark The lower time bound of a succeeding time step may not be smaller * than the upper time bound of its predecessor. Thus the list of time points is * always sorted by its lower time bounds. * @remark For the conversion between time step and time point the following assumption * is used.:\n * time step -> time point: time point is the lower time bound of the geometry indicated by step.\n * time point -> time step: associated time step is last step which lower time bound is smaller or equal then the time * point. * * \addtogroup geometry */ class MITKCORE_EXPORT ArbitraryTimeGeometry : public TimeGeometry { public: mitkClassMacro(ArbitraryTimeGeometry, TimeGeometry); ArbitraryTimeGeometry(); typedef ArbitraryTimeGeometry self; itkFactorylessNewMacro(Self); itkCloneMacro(Self); /** * \brief Returns the number of time steps. * * Returns the number of time steps for which * geometries are saved. The number of time steps * is also the upper bound of the time steps. The * minimum time steps is always 0. */ TimeStepType CountTimeSteps() const override; /** * \brief Returns the first time point for which the time geometry instance is valid. * * Returns the first valid time point for this geometry. It is the lower time bound of * the first step. The time point is given in ms. */ TimePointType GetMinimumTimePoint() const override; /** * \brief Returns the last time point for which the time geometry instance is valid * * Gives the last time point for which a valid geometry is saved in * this time geometry. It is the upper time bound of the last step. * The time point is given in ms. */ TimePointType GetMaximumTimePoint() const override; /** * \brief Returns the first time point for which the time geometry instance is valid. * * Returns the first valid time point for the given TimeStep. The time point * is given in ms. */ TimePointType GetMinimumTimePoint(TimeStepType step) const override; /** * \brief Returns the last time point for which the time geometry instance is valid * * Gives the last time point for the Geometry specified by the given TimeStep. The time point is given in ms. */ TimePointType GetMaximumTimePoint(TimeStepType step) const override; /** * \brief Get the time bounds (in ms) * it returns GetMinimumTimePoint() and GetMaximumTimePoint() results as bounds. */ TimeBounds GetTimeBounds() const override; /** * \brief Get the time bounds for the given TimeStep (in ms) */ TimeBounds GetTimeBounds(TimeStepType step) const override; /** * \brief Tests if a given time point is covered by this time geometry instance * * Returns true if a geometry can be returned for the given time * point (so it is within GetTimeBounds() and fails if not. * The time point must be given in ms. */ bool IsValidTimePoint(TimePointType timePoint) const override; /** * \brief Test for the given time step if a geometry is availible * * Returns true if a geometry is defined for the given time step. * Otherwise false is returned. * The time step is defined as positiv number. */ bool IsValidTimeStep(TimeStepType timeStep) const override; /** * \brief Converts a time step to a time point * * Converts a time step to a time point by using the time steps lower * time bound. * If the original time steps does not point to a valid geometry, * a time point is calculated that also does not point to a valid * geometry, but no exception is raised. */ TimePointType TimeStepToTimePoint(TimeStepType timeStep) const override; /** - * \brief Converts a time point to the corresponding time step - * - * Converts a time point to a time step in a way that - * the new time step indicates the same geometry as the time point. - * The associated time step is the last step which lower time bound - * is smaller or equal then the time point. - * If a negative invalid time point is given always time step 0 is - * returned. If a positive invalid time point is given the last time - * step will be returned. This is also true for time points that are - * exactly on the upper time bound. - */ + * \brief Converts a time point to the corresponding time step + * + * Converts a time point to a time step in a way that + * the new time step indicates the same geometry as the time point. + * The associated time step is the last step which lower time bound + * is smaller or equal then the time point. + * If a negative invalid time point is given always time step 0 is + * returned. If a positive invalid time point is given the last time + * step will be returned. This is also true for time points that are + * exactly on the upper time bound (the only exception is the final + * time step in case that HasCollapsedFinalTimeStep() is true). + */ TimeStepType TimePointToTimeStep(TimePointType timePoint) const override; /** * \brief Returns the geometry which corresponds to the given time step * * Returns a clone of the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. */ BaseGeometry::Pointer GetGeometryCloneForTimeStep(TimeStepType timeStep) const override; /** * \brief Returns the geometry which corresponds to the given time point * * Returns the geometry which defines the given time point. If * the given time point is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ BaseGeometry::Pointer GetGeometryForTimePoint(TimePointType timePoint) const override; /** * \brief Returns the geometry which corresponds to the given time step * * Returns the geometry which defines the given time step. If * the given time step is invalid an null-pointer is returned. * * If the returned geometry is changed this will affect the saved * geometry. */ BaseGeometry::Pointer GetGeometryForTimeStep(TimeStepType timeStep) const override; /** * \brief Tests if all necessary informations are set and the object is valid */ bool IsValid() const override; /** * \brief Initializes a new object with one time steps which contains an empty geometry. */ void Initialize() override; /** * \brief Expands the time geometry to the given number of time steps. * * Initializes the new time steps with empty geometries. This default geometries will behave like * ProportionalTimeGeometry. * Shrinking is not supported. The new steps will have the same duration like the last step before extension. */ void Expand(TimeStepType size) override; /** * \brief Replaces the geometry instances with clones of the passed geometry. * * Replaces the geometries of all time steps with clones of the passed * geometry. Replacement strategy depends on the implementation of TimeGeometry * sub class. * @remark The time points itself stays untouched. Use this method if you want * to change the spatial properties of a TimeGeometry and preserve the time * "grid". */ void ReplaceTimeStepGeometries(const BaseGeometry *geometry) override; /** * \brief Sets the geometry for the given time step * * If passed time step is not valid. Nothing will be changed. * @pre geometry must point to a valid instance. */ void SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) override; /** * \brief Makes a deep copy of the current object */ itk::LightObject::Pointer InternalClone() const override; void ClearAllGeometries(); /** Append the passed geometry to the time geometry. * @pre The passed geometry pointer must be valid. * @pre The minimumTimePoint must not be smaller than the maximum time point of the currently last time step. * Therefore time steps must not be overlapping in time. * @pre minimumTimePoint must not be larger then maximumTimePoint.*/ void AppendNewTimeStep(BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint); /** Same than AppendNewTimeStep. But clones geometry before adding it.*/ void AppendNewTimeStepClone(const BaseGeometry* geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint ); void ReserveSpaceForGeometries( TimeStepType numberOfGeometries ); void PrintSelf(std::ostream &os, itk::Indent indent) const override; + /** This is a helper that indicates problematic corner cases that often occure e.g. when loading + dynamic DICOM data. There the final time step is collapsed as min time bound and max time bound + have the same value. For a more detailed explanation why it happens please see: + https://phabricator.mitk.org/T24766#131411 and https://phabricator.mitk.org/T27259#203524 + */ + bool HasCollapsedFinalTimeStep() const; + protected: ~ArbitraryTimeGeometry() override; std::vector m_GeometryVector; std::vector m_MinimumTimePoints; std::vector m_MaximumTimePoints; }; // end class ArbitraryTimeGeometry } // end namespace MITK #endif // ArbitraryTimeGeometry_h diff --git a/Modules/Core/include/mitkBaseData.h b/Modules/Core/include/mitkBaseData.h index 21177dffe6..539a9e35fd 100644 --- a/Modules/Core/include/mitkBaseData.h +++ b/Modules/Core/include/mitkBaseData.h @@ -1,417 +1,422 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #define BASEDATA_H_HEADER_INCLUDED_C1EBB6FA #include #include "mitkBaseProcess.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #include "mitkOperationActor.h" #include "mitkPropertyList.h" #include "mitkTimeGeometry.h" #include namespace mitk { // class BaseProcess; //##Documentation //## @brief Base of all data objects //## //## Base of all data objects, e.g., images, contours, surfaces etc. Inherits //## from itk::DataObject and thus can be included in a pipeline. //## Inherits also from OperationActor and can be used as a destination for Undo + //## @remark Some derived classes may support the persistence of the Identifiable UID. + //** but it is no guaranteed feature and also depends on the format the data is stored in + //** as not all formats support storing of meta information. Please check the documentation + //** of the IFileReader and IFileWriter classes to see if the ID-persistance is supported. + //** MITK SceneIO supports the UID persistance for all BaseData derived classes. //## @ingroup Data class MITKCORE_EXPORT BaseData : public itk::DataObject, public OperationActor, public Identifiable, public IPropertyOwner { public: mitkClassMacroItkParent(BaseData, itk::DataObject); // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ const mitk::TimeGeometry *GetTimeGeometry() const { return m_TimeGeometry.GetPointer(); } /** * \brief Return the TimeGeometry of the data as const pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. * \deprecatedSince{2013_09} Please use GetTimeGeometry instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(const mitk::TimeGeometry *GetTimeSlicedGeometry() const) { return GetTimeGeometry(); } /** * @brief Return the TimeGeometry of the data as pointer. * * \warning No update will be called. Use GetUpdatedGeometry() if you cannot * be sure that the geometry is up-to-date. * * Normally used in GenerateOutputInformation of subclasses of BaseProcess. */ mitk::TimeGeometry *GetTimeGeometry() { return m_TimeGeometry.GetPointer(); } /** * @brief Return the TimeGeometry of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). */ const mitk::TimeGeometry *GetUpdatedTimeGeometry(); /** * @brief Return the TimeGeometry of the data. * * The method does not simply return the value of the m_TimeGeometry * member. Before doing this, it makes sure that the TimeGeometry * is up-to-date (by setting the update extent to largest possible and * calling UpdateOutputInformation). * \deprecatedSince{2013_09} Please use GetUpdatedTimeGeometry instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(const mitk::TimeGeometry *GetUpdatedTimeSliceGeometry()) { return GetUpdatedTimeGeometry(); } /** * \brief Expands the TimeGeometry to a number of TimeSteps. * * The method expands the TimeGeometry to the given number of TimeSteps, * filling newly created elements with empty geometries. Sub-classes should override * this method to handle the elongation of their data vectors, too. * Note that a shrinking is neither possible nor intended. */ virtual void Expand(unsigned int timeSteps); /** * \brief Return the BaseGeometry of the data at time \a t. * * The method does not simply return * m_TimeGeometry->GetGeometry(t). * Before doing this, it makes sure that the BaseGeometry is up-to-date * (by setting the update extent appropriately and calling * UpdateOutputInformation). * * @todo Appropriate setting of the update extent is missing. */ const mitk::BaseGeometry *GetUpdatedGeometry(int t = 0); //##Documentation //## @brief Return the geometry, which is a TimeGeometry, of the data //## as non-const pointer. //## //## \warning No update will be called. Use GetUpdatedGeometry() if you cannot //## be sure that the geometry is up-to-date. //## //## Normally used in GenerateOutputInformation of subclasses of BaseProcess. mitk::BaseGeometry *GetGeometry(int t = 0) const { if (m_TimeGeometry.IsNull()) return nullptr; return m_TimeGeometry->GetGeometryForTimeStep(t); } //##Documentation //## @brief Update the information for this BaseData (the geometry in particular) //## so that it can be used as an output of a BaseProcess. //## //## This method is used in the pipeline mechanism to propagate information and //## initialize the meta data associated with a BaseData. Any implementation //## of this method in a derived class is assumed to call its source's //## BaseProcess::UpdateOutputInformation() which determines modified //## times, LargestPossibleRegions, and any extra meta data like spacing, //## origin, etc. Default implementation simply call's it's source's //## UpdateOutputInformation(). //## \note Implementations of this methods in derived classes must take care //## that the geometry is updated by calling //## GetTimeGeometry()->UpdateInformation() //## \em after calling its source's BaseProcess::UpdateOutputInformation(). void UpdateOutputInformation() override; //##Documentation //## @brief Set the RequestedRegion to the LargestPossibleRegion. //## //## This forces a filter to produce all of the output in one execution //## (i.e. not streaming) on the next call to Update(). void SetRequestedRegionToLargestPossibleRegion() override = 0; //##Documentation //## @brief Determine whether the RequestedRegion is outside of the BufferedRegion. //## //## This method returns true if the RequestedRegion //## is outside the BufferedRegion (true if at least one pixel is //## outside). This is used by the pipeline mechanism to determine //## whether a filter needs to re-execute in order to satisfy the //## current request. If the current RequestedRegion is already //## inside the BufferedRegion from the previous execution (and the //## current filter is up to date), then a given filter does not need //## to re-execute bool RequestedRegionIsOutsideOfTheBufferedRegion() override = 0; //##Documentation //## @brief Verify that the RequestedRegion is within the LargestPossibleRegion. //## //## If the RequestedRegion is not within the LargestPossibleRegion, //## then the filter cannot possibly satisfy the request. This method //## returns true if the request can be satisfied (even if it will be //## necessary to process the entire LargestPossibleRegion) and //## returns false otherwise. This method is used by //## PropagateRequestedRegion(). PropagateRequestedRegion() throws a //## InvalidRequestedRegionError exception if the requested region is //## not within the LargestPossibleRegion. bool VerifyRequestedRegion() override = 0; //##Documentation //## @brief Copy information from the specified data set. //## //## This method is part of the pipeline execution model. By default, a //## BaseProcess will copy meta-data from the first input to all of its //## outputs. See ProcessObject::GenerateOutputInformation(). Each //## subclass of DataObject is responsible for being able to copy //## whatever meta-data it needs from another DataObject. //## The default implementation of this method copies the time sliced geometry //## and the property list of an object. If a subclass overrides this //## method, it should always call its superclass' version. void CopyInformation(const itk::DataObject *data) override; //##Documentation //## @brief Check whether the data has been initialized, i.e., //## at least the Geometry and other header data has been set //## //## \warning Set to \a true by default for compatibility reasons. //## Set m_Initialized=false in constructors of sub-classes that //## support distinction between initialized and uninitialized state. virtual bool IsInitialized() const; //##Documentation //## @brief Calls ClearData() and InitializeEmpty(); //## \warning Only use in subclasses that reimplemented these methods. //## Just calling Clear from BaseData will reset an object to a not initialized, //## invalid state. virtual void Clear(); //##Documentation //## @brief Check whether object contains data (at //## a specified time), e.g., a set of points may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmptyTimeStep(unsigned int t) const; //##Documentation //## @brief Check whether object contains data (at //## least at one point in time), e.g., a set of points //## may be empty //## //## \warning Returns IsInitialized()==false by default for //## compatibility reasons. Override in sub-classes that //## support distinction between empty/non-empty state. virtual bool IsEmpty() const; //##Documentation //## @brief Set the requested region from this data object to match the requested //## region of the data object passed in as a parameter. //## //## This method is implemented in the concrete subclasses of BaseData. void SetRequestedRegion(const itk::DataObject *data) override = 0; //##Documentation //##@brief overwrite if the Data can be called by an Interactor (StateMachine). //## //## Empty by default. Overwrite and implement all the necessary operations here //## and get the necessary information from the parameter operation. void ExecuteOperation(Operation *operation) override; /** * \brief Set the BaseGeometry of the data, which will be referenced (not copied!). * Assumes the data object has only 1 time step ( is a 3D object ) and creates a * new TimeGeometry which saves the given BaseGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedGeometry */ virtual void SetGeometry(BaseGeometry *aGeometry3D); /** * \brief Set the TimeGeometry of the data, which will be referenced (not copied!). * * @warning This method will normally be called internally by the sub-class of BaseData * during initialization. * \sa SetClonedTimeGeometry */ virtual void SetTimeGeometry(TimeGeometry *geometry); /** * \brief Set a clone of the provided Geometry as Geometry of the data. * Assumes the data object has only 1 time step ( is a 3D object ) and * creates a new TimeGeometry. If an TimeGeometry has already * been set for the object, it will be replaced after calling this function. * * \sa SetGeometry */ virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D); /** * \brief Set a clone of the provided TimeGeometry as TimeGeometry of the data. * * \sa SetGeometry */ virtual void SetClonedTimeGeometry(const TimeGeometry *geometry); //##Documentation //## @brief Set a clone of the provided geometry as BaseGeometry of a given time step. //## //## \sa SetGeometry virtual void SetClonedGeometry(const BaseGeometry *aGeometry3D, unsigned int time); //##Documentation //## @brief Get the data's property list //## @sa GetProperty //## @sa m_PropertyList mitk::PropertyList::Pointer GetPropertyList() const; //##Documentation //## @brief Set the data's property list //## @sa SetProperty //## @sa m_PropertyList void SetPropertyList(PropertyList *propertyList); //##Documentation //## @brief Get the property (instance of BaseProperty) with key @a propertyKey from the PropertyList, //## and set it to this, respectively; //## @sa GetPropertyList //## @sa m_PropertyList //## @sa m_MapOfPropertyLists mitk::BaseProperty::Pointer GetProperty(const char *propertyKey) const; void SetProperty(const char *propertyKey, BaseProperty *property); //##Documentation //## @brief Convenience method for setting the origin of //## the BaseGeometry instances of all time steps //## //## \warning Geometries contained in the BaseGeometry will //## \em not be changed, e.g. in case the BaseGeometry is a //## SlicedGeometry3D the origin will \em not be propagated //## to the contained slices. The sub-class SlicedData //## does this for the case that the SlicedGeometry3D is //## evenly spaced. virtual void SetOrigin(const Point3D &origin); /** \brief Get the process object that generated this data object. * * If there is no process object, then the data object has * been disconnected from the pipeline, or the data object * was created manually. (Note: we cannot use the GetObjectMacro() * defined in itkMacro because the mutual dependency of * DataObject and ProcessObject causes compile problems. Also, * a forward reference smart pointer is returned, not a smart pointer, * because of the circular dependency between the process and data object.) * * GetSource() returns a SmartPointer and not a WeakPointer * because it is assumed the code calling GetSource() wants to hold a * long term reference to the source. */ itk::SmartPointer GetSource() const; //##Documentation //## @brief Get the number of time steps from the TimeGeometry //## As the base data has not a data vector given by itself, the number //## of time steps is defined over the time sliced geometry. In sub classes, //## a better implementation could be over the length of the data vector. unsigned int GetTimeSteps() const { return m_TimeGeometry->CountTimeSteps(); } //##Documentation //## @brief Get the modified time of the last change of the contents //## this data object or its geometry. unsigned long GetMTime() const override; /** * \sa itk::ProcessObject::Graft */ void Graft(const DataObject *) override; protected: BaseData(); BaseData(const BaseData &other); ~BaseData() override; //##Documentation //## \brief Initialize the TimeGeometry for a number of time steps. //## The TimeGeometry is initialized empty and evenly timed. //## In many cases it will be necessary to overwrite this in sub-classes. virtual void InitializeTimeGeometry(unsigned int timeSteps = 1); /** * \brief Initialize the TimeGeometry for a number of time steps. * The TimeGeometry is initialized empty and evenly timed. * In many cases it will be necessary to overwrite this in sub-classes. * \deprecatedSince{2013_09} Please use GetUpdatedTimeGeometry instead: For additional information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(virtual void InitializeTimeSlicedGeometry(unsigned int timeSteps = 1)) { InitializeTimeGeometry(timeSteps); } //##Documentation //## @brief reset to non-initialized state, release memory virtual void ClearData(); //##Documentation //## @brief Pure virtual; Must be used in subclasses to get a data object to a //## valid state. Should at least create one empty object and call //## Superclass::InitializeTimeGeometry() to ensure an existing valid geometry virtual void InitializeEmpty() {} void PrintSelf(std::ostream &os, itk::Indent indent) const override; bool m_LastRequestedRegionWasOutsideOfTheBufferedRegion; mutable unsigned int m_SourceOutputIndexDuplicate; bool m_Initialized; private: //##Documentation //## @brief PropertyList, f.e. to hold pic-tags, tracking-data,.. //## PropertyList::Pointer m_PropertyList; TimeGeometry::Pointer m_TimeGeometry; }; } // namespace mitk #endif /* BASEDATA_H_HEADER_INCLUDED_C1EBB6FA */ diff --git a/Modules/Core/include/mitkBaseGeometry.h b/Modules/Core/include/mitkBaseGeometry.h index 7854b69e06..272521a625 100644 --- a/Modules/Core/include/mitkBaseGeometry.h +++ b/Modules/Core/include/mitkBaseGeometry.h @@ -1,761 +1,786 @@ /*============================================================================ 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 BaseGeometry_H_HEADER_INCLUDED #define BaseGeometry_H_HEADER_INCLUDED #include "mitkOperationActor.h" #include #include #include "itkScalableAffineTransform.h" #include "mitkNumericTypes.h" #include #include #include #include #include #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; namespace mitk { //##Documentation //## @brief Standard 3D-BoundingBox typedef //## //## Standard 3D-BoundingBox typedef to get rid of template arguments (3D, type). typedef itk::BoundingBox BoundingBox; //##Documentation //## @brief Standard typedef for time-bounds typedef itk::FixedArray TimeBounds; typedef itk::FixedArray FixedArrayType; typedef itk::AffineGeometryFrame AffineGeometryFrame3D; //##Documentation //## @brief BaseGeometry Describes the geometry of a data object //## //## The class holds //## \li a bounding box which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels), to be accessed by //## GetBoundingBox() //## \li a transform to convert intrinsic coordinates into a //## world-coordinate system with coordinates in millimeters //## and milliseconds (all are floating point values), to //## be accessed by GetIndexToWorldTransform() //## \li an origin and spacing to define the geometry //## //## BaseGeometry and its sub-classes allow converting between //## intrinsic coordinates (called index or unit coordinates) //## and world-coordinates (called world or mm coordinates), //## e.g. WorldToIndex. //## In case you need integer index coordinates, provide an //## mitk::Index3D (or itk::Index) as target variable to //## WorldToIndex, otherwise you will get a continuous index //## (floating point values). //## //## An important sub-class is SlicedGeometry3D, which descibes //## data objects consisting of slices, e.g., objects of type Image. //## Conversions between world coordinates (in mm) and unit coordinates //## (e.g., pixels in the case of an Image) can be performed. //## //## For more information on related classes, see \ref Geometry. //## //## BaseGeometry instances referring to an Image need a slightly //## different definition of corners, see SetImageGeometry. This //## is usualy automatically called by Image. //## //## BaseGeometry have to be initialized in the method GenerateOutputInformation() //## of BaseProcess (or CopyInformation/ UpdateOutputInformation of BaseData, //## if possible, e.g., by analyzing pic tags in Image) subclasses. See also //## itk::ProcessObject::GenerateOutputInformation(), //## itk::DataObject::CopyInformation() and //## itk::DataObject::UpdateOutputInformation(). //## //## At least, it can return the bounding box of the data object. //## //## The BaseGeometry class is an abstract class. The most simple implementation //## is the sublass Geometry3D. //## //## Rule: everything is in mm (ms) if not stated otherwise. //## @ingroup Geometry class MITKCORE_EXPORT BaseGeometry : public itk::Object, public OperationActor { public: mitkClassMacroItkParent(BaseGeometry, itk::Object); itkCloneMacro(Self); // ********************************** TypeDef ********************************** typedef GeometryTransformHolder::TransformType TransformType; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // ********************************** Origin, Spacing ********************************** //##Documentation //## @brief Get the origin, e.g. the upper-left corner of the plane const Point3D GetOrigin() const; //##Documentation //## @brief Set the origin, i.e. the upper-left corner of the plane //## void SetOrigin(const Point3D &origin); //##Documentation //## @brief Get the spacing (size of a pixel). //## const mitk::Vector3D GetSpacing() const; //##Documentation //## @brief Set the spacing (m_Spacing). //## //##The spacing is also changed in the IndexToWorldTransform. void SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief Get the origin as VnlVector //## //## \sa GetOrigin VnlVector GetOriginVnl() const; // ********************************** other functions ********************************** //##Documentation //## @brief Get the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkGetConstMacro(FrameOfReferenceID, unsigned int); //##Documentation //## @brief Set the DICOM FrameOfReferenceID referring to the //## used world coordinate system itkSetMacro(FrameOfReferenceID, unsigned int); itkGetConstMacro(IndexToWorldTransformLastModified, unsigned long); //##Documentation //## @brief Overload of function Modified() to prohibit several calls of Modified() using the ModifiedLock class. //## //## For the use of Modified(), see class ModifiedLock. void Modified() const override; friend class ModifiedLock; //##Documentation //## @brief Is this BaseGeometry in a state that is valid? //## //## This function returns always true in the BaseGeometry class. Other implementations are possible in subclasses. virtual bool IsValid() const; // ********************************** Initialize ********************************** //##Documentation //## @brief Initialize the BaseGeometry void Initialize(); void InitializeGeometry(Self *newGeometry) const; // ********************************** Transformations Set/Get ********************************** //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates mitk::AffineTransform3D *GetIndexToWorldTransform(); //##Documentation //## @brief Get the transformation used to convert from index //## to world coordinates const mitk::AffineTransform3D *GetIndexToWorldTransform() const; //## @brief Set the transformation used to convert from index //## to world coordinates. The spacing of the new transform is //## copied to m_spacing. void SetIndexToWorldTransform(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4.The spacing of //## the new transform is copied to m_spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix); //## @brief Set the transformation used to convert from index //## to world coordinates.This function keeps the original spacing. void SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform); //##Documentation //## @brief Convenience method for setting the ITK transform //## (m_IndexToWorldTransform) via an vtkMatrix4x4. This function keeps the original spacing. //## \sa SetIndexToWorldTransform void SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix); //## Get the Vtk Matrix which describes the transform. vtkMatrix4x4 *GetVtkMatrix(); //##Documentation //## @brief Get the m_IndexToWorldTransform as a vtkLinearTransform vtkLinearTransform *GetVtkTransform() const; //##Documentation //## @brief Set the transform to identity, the spacing to 1 and origin to 0 //## void SetIdentity(); // ********************************** Transformations ********************************** //##Documentation //## @brief Compose new IndexToWorldTransform with a given transform. //## //## This method composes m_IndexToWorldTransform with another transform, //## modifying self to be the composition of self and other. //## If the argument pre is true, then other is precomposed with self; //## that is, the resulting transformation consists of first applying //## other to the source, followed by self. If pre is false or omitted, //## then other is post-composed with self; that is the resulting //## transformation consists of first applying self to the source, //## followed by other. //## This method also changes m_spacing. void Compose(const TransformType *other, bool pre = false); //##Documentation //## @brief Compose new IndexToWorldTransform with a given vtkMatrix4x4. //## //## Converts the vtkMatrix4x4 into a itk-transform and calls the previous method. void Compose(const vtkMatrix4x4 *vtkmatrix, bool pre = false); //##Documentation //## @brief Translate the origin by a vector //## void Translate(const Vector3D &vector); //##Documentation //##@brief executes affine operations (translate, rotate, scale) void ExecuteOperation(Operation *operation) override; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (continuous!) index coordinates //## \warning If you need (discrete) integer index coordinates (e.g., for iterating easily over an image), //## use WorldToIndex(const mitk::Point3D& pt_mm, itk::Index &index). //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em point to (discrete!) index coordinates. //## This method rounds to integer indices! //## For further information about coordinates types, please see the Geometry documentation template void WorldToIndex(const mitk::Point3D &pt_mm, itk::Index &index) const { typedef itk::Index IndexType; mitk::Point3D pt_units; this->WorldToIndex(pt_mm, pt_units); int i, dim = index.GetIndexDimension(); if (dim > 3) { index.Fill(0); dim = 3; } for (i = 0; i < dim; ++i) { index[i] = itk::Math::RoundHalfIntegerUp(pt_units[i]); } } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const; //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const; //##Documentation //## @brief Convert (discrete) index coordinates of a \em point to world coordinates (in mm) //## For further information about coordinates types, please see the Geometry documentation template void IndexToWorld(const itk::Index &index, mitk::Point3D &pt_mm) const { mitk::Point3D pt_units; pt_units.Fill(0); int i, dim = index.GetIndexDimension(); if (dim > 3) { dim = 3; } for (i = 0; i < dim; ++i) { pt_units[i] = index[i]; } IndexToWorld(pt_units, pt_mm); } //##Documentation //## @brief Convert (continuous or discrete) index coordinates of a \em vector //## \a vec_units to world coordinates (in mm) //## @deprecated First parameter (Point3D) is not used. If possible, please use void IndexToWorld(const // mitk::Vector3D& vec_units, mitk::Vector3D& vec_mm) const. //## For further information about coordinates types, please see the Geometry documentation void IndexToWorld(const mitk::Point3D &atPt3d_units, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const; //##Documentation //## @brief Convert world coordinates (in mm) of a \em vector //## \a vec_mm to (continuous!) index coordinates. //## @deprecated First parameter (Point3D) is not used. If possible, please use void WorldToIndex(const // mitk::Vector3D& vec_mm, mitk::Vector3D& vec_units) const. //## For further information about coordinates types, please see the Geometry documentation void WorldToIndex(const mitk::Point3D &atPt3d_mm, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const; //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert ITK physical coordinates of a \em point (in mm, //## but without a rotation) into MITK world coordinates (in mm) //## //## For more information, see WorldToItkPhysicalPoint. template void ItkPhysicalPointToWorld(const itk::Point &itkPhysicalPoint, mitk::Point3D &pt_mm) const { mitk::vtk2itk(itkPhysicalPoint, pt_mm); } //##Documentation //## @brief Deprecated for use with ITK version 3.10 or newer. //## Convert world coordinates (in mm) of a \em point to //## ITK physical coordinates (in mm, but without a possible rotation) //## //## This method is useful if you have want to access an mitk::Image //## via an itk::Image. ITK v3.8 and older did not support rotated (tilted) //## images, i.e., ITK images are always parallel to the coordinate axes. //## When accessing a (possibly rotated) mitk::Image via an itk::Image //## the rotational part of the transformation in the BaseGeometry is //## simply discarded; in other word: only the origin and spacing is //## used by ITK, not the complete matrix available in MITK. //## With WorldToItkPhysicalPoint you can convert an MITK world //## coordinate (including the rotation) into a coordinate that //## can be used with the ITK image as a ITK physical coordinate //## (excluding the rotation). template void WorldToItkPhysicalPoint(const mitk::Point3D &pt_mm, itk::Point &itkPhysicalPoint) const { mitk::vtk2itk(pt_mm, itkPhysicalPoint); } // ********************************** BoundingBox ********************************** /** Get the bounding box */ itkGetConstObjectMacro(BoundingBox, BoundingBoxType); // a bit of a misuse, but we want only doxygen to see the following: #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get bounding box (in index/unit coordinates) itkGetConstObjectMacro(BoundingBox, BoundingBoxType); //##Documentation //## @brief Get bounding box (in index/unit coordinates) as a BoundsArrayType const BoundsArrayType GetBounds() const; #endif const BoundsArrayType GetBounds() const; //##Documentation //## \brief Set the bounding box (in index/unit coordinates) //## //## Only possible via the BoundsArray to make clear that a //## copy of the bounding-box is stored, not a reference to it. void SetBounds(const BoundsArrayType &bounds); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a float array void SetFloatBounds(const float bounds[6]); //##Documentation //## @brief Set the bounding box (in index/unit coordinates) via a double array void SetFloatBounds(const double bounds[6]); //##Documentation //## @brief Get a VnlVector along bounding-box in the specified //## @a direction, length is spacing //## //## \sa GetAxisVector VnlVector GetMatrixColumn(unsigned int direction) const; //##Documentation //## @brief Calculates a bounding-box around the geometry relative //## to a coordinate system defined by a transform //## mitk::BoundingBox::Pointer CalculateBoundingBoxRelativeToTransform(const mitk::AffineTransform3D *transform) const; //##Documentation //## @brief Set the time bounds (in ms) // void SetTimeBounds(const TimeBounds& timebounds); // ********************************** Geometry ********************************** #ifdef DOXYGEN_SKIP //##Documentation //## @brief Get the extent of the bounding box (in index/unit coordinates) //## //## To access the extent in mm use GetExtentInMM ScalarType GetExtent(unsigned int direction) const; #endif /** Get the extent of the bounding box */ ScalarType GetExtent(unsigned int direction) const; //##Documentation //## @brief Get the extent of the bounding-box in the specified @a direction in mm //## //## Equals length of GetAxisVector(direction). ScalarType GetExtentInMM(int direction) const; //##Documentation //## @brief Get vector along bounding-box in the specified @a direction in mm //## //## The length of the vector is the size of the bounding-box in the //## specified @a direction in mm //## \sa GetMatrixColumn Vector3D GetAxisVector(unsigned int direction) const; //##Documentation //## @brief Checks, if the given geometry can be converted to 2D without information loss //## e.g. when a 2D image is saved, the matrix is usually cropped to 2x2, and when you load it back to MITK //## it will be filled with standard values. This function checks, if information would be lost during this //## procedure virtual bool Is2DConvertable(); //##Documentation //## @brief Get the center of the bounding-box in mm //## Point3D GetCenter() const; //##Documentation //## @brief Get the squared length of the diagonal of the bounding-box in mm //## double GetDiagonalLength2() const; //##Documentation //## @brief Get the length of the diagonal of the bounding-box in mm //## double GetDiagonalLength() const; //##Documentation //## @brief Get the position of the corner number \a id (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(int id) const; //##Documentation //## @brief Get the position of a corner (in world coordinates) //## //## See SetImageGeometry for how a corner is defined on images. Point3D GetCornerPoint(bool xFront = true, bool yFront = true, bool zFront = true) const; //##Documentation //## @brief Set the extent of the bounding-box in the specified @a direction in mm //## //## @note This changes the matrix in the transform, @a not the bounds, which are given in units! void SetExtentInMM(int direction, ScalarType extentInMM); //##Documentation //## @brief Test whether the point \a p (world coordinates in mm) is //## inside the bounding box bool IsInside(const mitk::Point3D &p) const; //##Documentation //## @brief Test whether the point \a p ((continous!)index coordinates in units) is //## inside the bounding box bool IsIndexInside(const mitk::Point3D &index) const; //##Documentation //## @brief Convenience method for working with ITK indices template bool IsIndexInside(const itk::Index &index) const { int i, dim = index.GetIndexDimension(); Point3D pt_index; pt_index.Fill(0); for (i = 0; i < dim; ++i) { pt_index[i] = index[i]; } return IsIndexInside(pt_index); } // ********************************* Image Geometry ******************************** //##Documentation //## @brief When switching from an Image Geometry to a normal Geometry (and the other way around), you have to //change // the origin as well (See Geometry Documentation)! This function will change the "isImageGeometry" bool flag and // changes the origin respectively. virtual void ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry); //##Documentation //## @brief Is this an ImageGeometry? //## //## For more information, see SetImageGeometry itkGetConstMacro(ImageGeometry, bool) //##Documentation //## @brief Define that this BaseGeometry is refering to an Image //## //## A geometry referring to an Image needs a slightly different //## definition of the position of the corners (see GetCornerPoint). //## The position of a voxel is defined by the position of its center. //## If we would use the origin (position of the (center of) the first //## voxel) as a corner and display this point, it would seem to be //## \em not at the corner but a bit within the image. Even worse for //## the opposite corner of the image: here the corner would appear //## outside the image (by half of the voxel diameter). Thus, we have //## to correct for this and to be able to do that, we need to know //## that the BaseGeometry is referring to an Image. itkSetMacro(ImageGeometry, bool); itkBooleanMacro(ImageGeometry); const GeometryTransformHolder *GetGeometryTransformHolder() const; protected: // ********************************** Constructor ********************************** BaseGeometry(); BaseGeometry(const BaseGeometry &other); ~BaseGeometry() override; //##Documentation //## @brief clones the geometry //## //## Overwrite in all sub-classes. //## Normally looks like: //## \code //## Self::Pointer newGeometry = new Self(*this); //## newGeometry->UnRegister(); //## return newGeometry.GetPointer(); //## \endcode itk::LightObject::Pointer InternalClone() const override = 0; void PrintSelf(std::ostream &os, itk::Indent indent) const override; static const std::string GetTransformAsString(TransformType *transformType); itkGetConstMacro(NDimensions, unsigned int); bool IsBoundingBoxNull() const; bool IsIndexToWorldTransformNull() const; void SetVtkMatrixDeepCopy(vtkTransform *vtktransform); void _SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing = false); //##Documentation //## @brief PreSetSpacing //## //## These virtual function allows a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use //## {Superclass::PreSetSpacing();}; virtual void PreSetSpacing(const mitk::Vector3D & /*aSpacing*/){}; //##Documentation //## @brief CheckBounds //## //## This function is called in SetBounds. Assertions can be implemented in this function (see PlaneGeometry.cpp). //## If you implement this function in a subclass, make sure, that all classes were your class inherits from //## have an implementation of CheckBounds //## (e.g. inheritance BaseGeometry <- A <- B. Implementation of CheckBounds in class B needs implementation in A as // well!) virtual void CheckBounds(const BoundsArrayType & /*bounds*/){}; //##Documentation //## @brief CheckIndexToWorldTransform //## //## This function is called in SetIndexToWorldTransform. Assertions can be implemented in this function (see // PlaneGeometry.cpp). //## In Subclasses of BaseGeometry, implement own conditions or call Superclass::CheckBounds(bounds);. virtual void CheckIndexToWorldTransform(mitk::AffineTransform3D * /*transform*/){}; private: GeometryTransformHolder *m_GeometryTransform; void InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry); //##Documentation //## @brief Bounding Box, which is axes-parallel in intrinsic coordinates //## (often integer indices of pixels) BoundingBoxPointer m_BoundingBox; unsigned int m_FrameOfReferenceID; // mitk::TimeBounds m_TimeBounds; static const unsigned int m_NDimensions = 3; mutable TransformType::Pointer m_InvertedTransform; mutable unsigned long m_IndexToWorldTransformLastModified; bool m_ImageGeometry; //##Documentation //## @brief ModifiedLockFlag is used to prohibit the call of Modified() //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the ModifiedLock class! bool m_ModifiedLockFlag; //##Documentation //## @brief ModifiedcalledFlag is used to collect calls of Modified(). //## //## For the use of this Flag, see class ModifiedLock. This flag should only be set //## by the Modified() function! mutable bool m_ModifiedCalledFlag; }; // ********************************** Equal Functions ********************************** // // Static compare functions mainly for testing // /** * @brief Equal A function comparing two geometries for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const * mitk::mitk::BaseGeometry& g1, const mitk::BaseGeometry& g2) instead. * * @ingroup MITKTestingAPI * * The function compares the spacing, origin, axisvectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * If you want to use different tolarance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ DEPRECATED(MITKCORE_EXPORT bool Equal( const mitk::BaseGeometry *leftHandSide, const mitk::BaseGeometry *rightHandSide, ScalarType eps, bool verbose)); /** * @brief Equal A function comparing two geometries for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the spacing, origin, axisvectors, extents, the matrix of the * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * If you want to use different tolarance values for different parts of the geometry, feel free to use * the other comparison methods and write your own implementation of Equal. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType eps, bool verbose); /** * @brief Equal A function comparing two transforms (TransformType) for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const * mitk::mitk::BaseGeometry::TransformType& t1, const mitk::BaseGeometry::TransformType& t2) instead. * * @ingroup MITKTestingAPI * * The function compares the IndexToWorldTransform (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ DEPRECATED(MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType *leftHandSide, const mitk::BaseGeometry::TransformType *rightHandSide, ScalarType eps, bool verbose)); /** * @brief Equal A function comparing two transforms (TransformType) for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the IndexToWorldTransform (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose); /** * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. * @warning This method is deprecated and will not be available in the future. Use the \a bool mitk::Equal(const * mitk::mitk::BaseGeometry::BoundingBoxType& b1, const mitk::BaseGeometry::BoundingBoxType& b2) instead. * * @ingroup MITKTestingAPI * * The function compares the bounds (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ DEPRECATED(MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::BoundingBoxType *leftHandSide, const mitk::BaseGeometry::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose)); /** * @brief Equal A function comparing two bounding boxes (BoundingBoxType) for beeing identical. * * @ingroup MITKTestingAPI * * The function compares the bounds (elementwise). * * The parameter eps is a tolarence value for all methods which are internally used for comparion. * @param rightHandSide Compare this against leftHandSide. * @param leftHandSide Compare this against rightHandSide. * @param eps Tolarence for comparison. You can use mitk::eps in most cases. * @param verbose Flag indicating if the user wants detailed console output or not. * @return True, if all comparison are true. False in any other case. */ MITKCORE_EXPORT bool Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose); + + /** + * @brief A function checks if a test geometry is a sub geometry of + * a given reference geometry. + * + * Sub geometry means that both geometries have the same voxel grid (same spacing, same axes, + * orgin is on voxel grid), but the bounding box of the checked geometry is contained or equal + * to the bounding box of the reference geometry.\n + * By this definition equal geometries are always sub geometries of each other. + * + * The function checks the spacing, origin, axis vectors, extents, the matrix of the + * IndexToWorldTransform (elementwise), the bounding (elementwise) and the ImageGeometry flag. + * + * The parameter eps is a tolarence value for all methods which are internally used for comparison. + * @param testGeo Geometry that should be checked if it is a sub geometry of referenceGeo. + * @param referenceGeo Geometry that should contain testedGeometry as sub geometry. + * @param eps Tolarence for comparison. You can use mitk::eps in most cases. + * @param verbose Flag indicating if the user wants detailed console output or not. + * @return True, if all comparison are true. False otherwise. + */ + MITKCORE_EXPORT bool IsSubGeometry(const mitk::BaseGeometry& testGeo, + const mitk::BaseGeometry& referenceGeo, + ScalarType eps, + bool verbose); + } // namespace mitk #endif /* BaseGeometry_H_HEADER_INCLUDED */ diff --git a/Modules/Core/include/mitkBaseRenderer.h b/Modules/Core/include/mitkBaseRenderer.h index 322bc8577d..5b14a4ec2d 100644 --- a/Modules/Core/include/mitkBaseRenderer.h +++ b/Modules/Core/include/mitkBaseRenderer.h @@ -1,532 +1,532 @@ /*============================================================================ 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 BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 #define BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 #include "mitkCameraRotationController.h" #include "mitkDataStorage.h" #include "mitkPlaneGeometry.h" #include "mitkPlaneGeometryData.h" #include "mitkSliceNavigationController.h" #include "mitkTimeGeometry.h" #include "mitkBindDispatcherInteractor.h" #include "mitkDispatcher.h" #include #include #include #include // DEPRECATED #include namespace mitk { class NavigationController; class SliceNavigationController; class CameraRotationController; class CameraController; class DataStorage; class Mapper; class BaseLocalStorageHandler; class KeyEvent; //##Documentation //## @brief Organizes the rendering process //## //## Organizes the rendering process. A Renderer contains a reference to a //## DataStorage and asks the mappers of the data objects to render //## the data into the renderwindow it is associated to. //## //## \#Render() checks if rendering is currently allowed by calling //## RenderWindow::PrepareRendering(). Initialization of a rendering context //## can also be performed in this method. //## //## The actual rendering code has been moved to \#Repaint() //## Both \#Repaint() and \#Update() are declared protected now. //## //## Note: Separation of the Repaint and Update processes (rendering vs //## creating a vtk prop tree) still needs to be worked on. The whole //## rendering process also should be reworked to use VTK based classes for //## both 2D and 3D rendering. //## @ingroup Renderer class MITKCORE_EXPORT BaseRenderer : public itk::Object { public: typedef std::map BaseRendererMapType; static BaseRendererMapType baseRendererMap; static BaseRenderer *GetInstance(vtkRenderWindow *renWin); static void AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer); static void RemoveInstance(vtkRenderWindow *renWin); static BaseRenderer *GetByName(const std::string &name); static vtkRenderWindow *GetRenderWindowByName(const std::string &name); #pragma GCC visibility push(default) itkEventMacro(RendererResetEvent, itk::AnyEvent); #pragma GCC visibility pop /** Standard class typedefs. */ mitkClassMacroItkParent(BaseRenderer, itk::Object); BaseRenderer(const char *name = nullptr, vtkRenderWindow *renWin = nullptr); //##Documentation //## @brief MapperSlotId defines which kind of mapper (e.g. 2D or 3D) should be used. typedef int MapperSlotId; enum StandardMapperSlot { Standard2D = 1, Standard3D = 2 }; //##Documentation //## @brief Possible view directions for render windows. enum class ViewDirection { AXIAL = 0, SAGITTAL, CORONAL, THREE_D }; virtual void SetDataStorage(DataStorage *storage); ///< set the datastorage that will be used for rendering //##Documentation //## return the DataStorage that is used for rendering virtual DataStorage::Pointer GetDataStorage() const { return m_DataStorage.GetPointer(); } //##Documentation //## @brief Access the RenderWindow into which this renderer renders. vtkRenderWindow *GetRenderWindow() const { return m_RenderWindow; } vtkRenderer *GetVtkRenderer() const { return m_VtkRenderer; } //##Documentation //## @brief Returns the Dispatcher which handles Events for this BaseRenderer Dispatcher::Pointer GetDispatcher() const; //##Documentation //## @brief Default mapper id to use. static const MapperSlotId defaultMapper; //##Documentation //## @brief Do the rendering and flush the result. virtual void Paint(); //##Documentation //## @brief Initialize the RenderWindow. Should only be called from RenderWindow. virtual void Initialize(); //##Documentation //## @brief Called to inform the renderer that the RenderWindow has been resized. virtual void Resize(int w, int h); //##Documentation //## @brief Initialize the renderer with a RenderWindow (@a renderwindow). virtual void InitRenderer(vtkRenderWindow *renderwindow); //##Documentation //## @brief Set the initial size. Called by RenderWindow after it has become //## visible for the first time. virtual void InitSize(int w, int h); //##Documentation //## @brief Draws a point on the widget. //## Should be used during conferences to show the position of the remote mouse virtual void DrawOverlayMouse(Point2D &p2d); //##Documentation //## @brief Set/Get the WorldGeometry (m_WorldGeometry) for 3D and 2D rendering, that describing the //## (maximal) area to be rendered. //## //## Depending of the type of the passed BaseGeometry more or less information can be extracted: //## \li if it is a PlaneGeometry (which is a sub-class of BaseGeometry), m_CurrentWorldPlaneGeometry is //## also set to point to it. m_WorldTimeGeometry is set to nullptr. //## \li if it is a TimeGeometry, m_WorldTimeGeometry is also set to point to it. //## If m_WorldTimeGeometry contains instances of SlicedGeometry3D, m_CurrentWorldPlaneGeometry is set to //## one of geometries stored in the SlicedGeometry3D according to the value of m_Slice; otherwise //## a PlaneGeometry describing the top of the bounding-box of the BaseGeometry is set as the //## m_CurrentWorldPlaneGeometry. //## \li otherwise a PlaneGeometry describing the top of the bounding-box of the BaseGeometry //## is set as the m_CurrentWorldPlaneGeometry. m_WorldTimeGeometry is set to nullptr. //## @todo add calculation of PlaneGeometry describing the top of the bounding-box of the BaseGeometry //## when the passed BaseGeometry is not sliced. //## \sa m_WorldGeometry //## \sa m_WorldTimeGeometry //## \sa m_CurrentWorldPlaneGeometry virtual void SetWorldGeometry3D(const BaseGeometry *geometry); virtual void SetWorldTimeGeometry(const mitk::TimeGeometry *geometry); /** * \deprecatedSince{2013_09} Please use TimeGeometry instead of TimeSlicedGeometry. For more information see * http://www.mitk.org/Development/Refactoring%20of%20the%20Geometry%20Classes%20-%20Part%201 */ DEPRECATED(void SetWorldGeometry3D(TimeSlicedGeometry *geometry)); itkGetConstObjectMacro(WorldTimeGeometry, TimeGeometry); //##Documentation //## @brief Get the current 3D-worldgeometry (m_CurrentWorldGeometry) used for 3D-rendering itkGetConstObjectMacro(CurrentWorldGeometry, BaseGeometry); //##Documentation //## @brief Get the current 2D-worldgeometry (m_CurrentWorldPlaneGeometry) used for 2D-rendering itkGetConstObjectMacro(CurrentWorldPlaneGeometry, PlaneGeometry) /** * \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometry */ DEPRECATED(const PlaneGeometry *GetCurrentWorldGeometry2D()) { return GetCurrentWorldPlaneGeometry(); }; //##Documentation //## Calculates the bounds of the DataStorage (if it contains any valid data), //## creates a geometry from these bounds and sets it as world geometry of the renderer. //## //## Call this method to re-initialize the renderer to the current DataStorage //## (e.g. after loading an additional dataset), to ensure that the view is //## aligned correctly. //## \warn This is not implemented yet. virtual bool SetWorldGeometryToDataStorageBounds() { return false; } //##Documentation //## @brief Set/Get m_Slice which defines together with m_TimeStep the 2D geometry //## stored in m_WorldTimeGeometry used as m_CurrentWorldPlaneGeometry //## //## \sa m_Slice virtual void SetSlice(unsigned int slice); itkGetConstMacro(Slice, unsigned int); //##Documentation //## @brief Set/Get m_TimeStep which defines together with m_Slice the 2D geometry //## stored in m_WorldTimeGeometry used as m_CurrentWorldPlaneGeometry //## //## \sa m_TimeStep virtual void SetTimeStep(unsigned int timeStep); itkGetConstMacro(TimeStep, unsigned int); //##Documentation //## @brief Get the time-step of a BaseData object which //## exists at the time of the currently displayed content //## //## Returns -1 or mitk::BaseData::m_TimeSteps if there //## is no data at the current time. //## \sa GetTimeStep, m_TimeStep int GetTimeStep(const BaseData *data) const; //##Documentation //## @brief Get the time in ms of the currently displayed content //## //## \sa GetTimeStep, m_TimeStep ScalarType GetTime() const; //##Documentation //## @brief SetWorldGeometry is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometrySendEvent virtual void SetGeometry(const itk::EventObject &geometrySliceEvent); //##Documentation //## @brief UpdateWorldGeometry is called to re-read the 2D geometry from the //## slice navigation controller virtual void UpdateGeometry(const itk::EventObject &geometrySliceEvent); //##Documentation //## @brief SetSlice is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometrySliceEvent virtual void SetGeometrySlice(const itk::EventObject &geometrySliceEvent); //##Documentation //## @brief SetTimeStep is called according to the geometrySliceEvent, //## which is supposed to be a SliceNavigationController::GeometryTimeEvent virtual void SetGeometryTime(const itk::EventObject &geometryTimeEvent); //##Documentation //## @brief Get a DataNode pointing to a data object containing the current 2D-worldgeometry // m_CurrentWorldPlaneGeometry (for 2D rendering) itkGetObjectMacro(CurrentWorldPlaneGeometryNode, DataNode) /** * \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometryNode */ DEPRECATED(DataNode *GetCurrentWorldGeometry2DNode()) { return GetCurrentWorldPlaneGeometryNode(); }; //##Documentation //## @brief Sets timestamp of CurrentWorldPlaneGeometry and forces so reslicing in that renderwindow void SendUpdateSlice(); //##Documentation //## @brief Get timestamp of last call of SetCurrentWorldPlaneGeometry unsigned long GetCurrentWorldPlaneGeometryUpdateTime() { return m_CurrentWorldPlaneGeometryUpdateTime; } /** * \deprecatedSince{2014_10} Please use GetCurrentWorldPlaneGeometryUpdateTime */ DEPRECATED(unsigned long GetCurrentWorldGeometry2DUpdateTime()) { return GetCurrentWorldPlaneGeometryUpdateTime(); }; //##Documentation //## @brief Get timestamp of last change of current TimeStep unsigned long GetTimeStepUpdateTime() { return m_TimeStepUpdateTime; } //##Documentation //## @brief Perform a picking: find the x,y,z world coordinate of a //## display x,y coordinate. //## @warning Has to be overwritten in subclasses for the 3D-case. //## //## Implemented here only for 2D-rendering virtual void PickWorldPoint(const Point2D &diplayPosition, Point3D &worldPosition) const = 0; /** \brief Determines the object (mitk::DataNode) closest to the current * position by means of picking * * \warning Implementation currently empty for 2D rendering; intended to be * implemented for 3D renderers */ virtual DataNode *PickObject(const Point2D & /*displayPosition*/, Point3D & /*worldPosition*/) const { return nullptr; } //##Documentation //## @brief Get the MapperSlotId to use. itkGetMacro(MapperID, MapperSlotId); itkGetConstMacro(MapperID, MapperSlotId); //##Documentation //## @brief Set the MapperSlotId to use. - itkSetMacro(MapperID, MapperSlotId); + virtual void SetMapperID(MapperSlotId id); virtual int *GetSize() const; virtual int *GetViewportSize() const; void SetSliceNavigationController(SliceNavigationController *SlicenavigationController); itkGetObjectMacro(CameraController, CameraController); itkGetObjectMacro(SliceNavigationController, SliceNavigationController); itkGetObjectMacro(CameraRotationController, CameraRotationController); itkGetMacro(EmptyWorldGeometry, bool); //##Documentation //## @brief Tells if the displayed region is shifted and rescaled if the render window is resized. itkGetMacro(KeepDisplayedRegion, bool) //##Documentation //## @brief Tells if the displayed region should be shifted and rescaled if the render window is resized. itkSetMacro(KeepDisplayedRegion, bool); //##Documentation //## @brief get the name of the Renderer //## @note const char *GetName() const { return m_Name.c_str(); } //##Documentation //## @brief get the x_size of the RendererWindow //## @note int GetSizeX() const { return GetSize()[0]; } //##Documentation //## @brief get the y_size of the RendererWindow //## @note int GetSizeY() const { return GetSize()[1]; } const double *GetBounds() const; void RequestUpdate(); void ForceImmediateUpdate(); /** Returns number of mappers which are visible and have level-of-detail * rendering enabled */ unsigned int GetNumberOfVisibleLODEnabledMappers() const; //##Documentation //## @brief This method converts a display point to the 3D world index //## using the geometry of the renderWindow. void DisplayToWorld(const Point2D &displayPoint, Point3D &worldIndex) const; //##Documentation //## @brief This method converts a display point to the 2D world index, mapped onto the display plane //## using the geometry of the renderWindow. void DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const; //##Documentation //## @brief This method converts a 3D world index to the display point //## using the geometry of the renderWindow. void WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const; //##Documentation //## @brief This method converts a 3D world index to the point on the viewport //## using the geometry of the renderWindow. void WorldToView(const Point3D &worldIndex, Point2D &viewPoint) const; //##Documentation //## @brief This method converts a 2D plane coordinate to the display point //## using the geometry of the renderWindow. void PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const; //##Documentation //## @brief This method converts a 2D plane coordinate to the point on the viewport //## using the geometry of the renderWindow. void PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const; double GetScaleFactorMMPerDisplayUnit() const; Point2D GetDisplaySizeInMM() const; Point2D GetViewportSizeInMM() const; Point2D GetOriginInMM() const; itkGetConstMacro(ConstrainZoomingAndPanning, bool) virtual void SetConstrainZoomingAndPanning(bool constrain); /** * \brief Provides (1) world coordinates for a given mouse position and (2) * translates mousePosition to Display coordinates * \deprecated Map2DRendererPositionTo3DWorldPosition is deprecated. Please use DisplayToWorld instead. */ DEPRECATED(virtual Point3D Map2DRendererPositionTo3DWorldPosition(const Point2D &mousePosition) const); protected: ~BaseRenderer() override; //##Documentation //## @brief Call update of all mappers. To be implemented in subclasses. virtual void Update() = 0; vtkRenderWindow *m_RenderWindow; vtkRenderer *m_VtkRenderer; //##Documentation //## @brief MapperSlotId to use. Defines which kind of mapper (e.g., 2D or 3D) shoud be used. MapperSlotId m_MapperID; //##Documentation //## @brief The DataStorage that is used for rendering. DataStorage::Pointer m_DataStorage; //##Documentation //## @brief Timestamp of last call of Update(). unsigned long m_LastUpdateTime; //##Documentation //## @brief CameraController for 3D rendering //## @note preliminary. itk::SmartPointer m_CameraController; SliceNavigationController::Pointer m_SliceNavigationController; CameraRotationController::Pointer m_CameraRotationController; //##Documentation //## @brief Sets m_CurrentWorldPlaneGeometry virtual void SetCurrentWorldPlaneGeometry(const PlaneGeometry *geometry2d); /** * \deprecatedSince{2014_10} Please use SetCurrentWorldPlaneGeometry */ DEPRECATED(void SetCurrentWorldGeometry2D(PlaneGeometry *geometry2d)) { SetCurrentWorldPlaneGeometry(geometry2d); }; //##Documentation //## @brief Sets m_CurrentWorldGeometry virtual void SetCurrentWorldGeometry(const BaseGeometry *geometry); private: //##Documentation //## m_WorldTimeGeometry is set by SetWorldGeometry if the passed BaseGeometry is a //## TimeGeometry (or a sub-class of it). If it contains instances of SlicedGeometry3D, //## m_Slice and m_TimeStep (set via SetSlice and SetTimeStep, respectively) define //## which 2D geometry stored in m_WorldTimeGeometry (if available) //## is used as m_CurrentWorldPlaneGeometry. //## \sa m_CurrentWorldPlaneGeometry TimeGeometry::ConstPointer m_WorldTimeGeometry; //##Documentation //## Pointer to the current 3D-worldgeometry. BaseGeometry::ConstPointer m_CurrentWorldGeometry; //##Documentation //## Pointer to the current 2D-worldgeometry. The 2D-worldgeometry //## describes the maximal area (2D manifold) to be rendered in case we //## are doing 2D-rendering. //## It is const, since we are not allowed to change it (it may be taken //## directly from the geometry of an image-slice and thus it would be //## very strange when suddenly the image-slice changes its geometry). PlaneGeometry::Pointer m_CurrentWorldPlaneGeometry; //##Documentation //## Defines together with m_Slice which 2D geometry stored in m_WorldTimeGeometry //## is used as m_CurrentWorldPlaneGeometry: m_WorldTimeGeometry->GetPlaneGeometry(m_Slice, m_TimeStep). //## \sa m_WorldTimeGeometry unsigned int m_Slice; //##Documentation //## Defines together with m_TimeStep which 2D geometry stored in m_WorldTimeGeometry //## is used as m_CurrentWorldPlaneGeometry: m_WorldTimeGeometry->GetPlaneGeometry(m_Slice, m_TimeStep). //## \sa m_WorldTimeGeometry unsigned int m_TimeStep; //##Documentation //## @brief timestamp of last call of SetWorldGeometry itk::TimeStamp m_CurrentWorldPlaneGeometryUpdateTime; //##Documentation //## @brief timestamp of last change of the current time step itk::TimeStamp m_TimeStepUpdateTime; //##Documentation //## @brief Helper class which establishes connection between Interactors and Dispatcher via a common DataStorage. BindDispatcherInteractor *m_BindDispatcherInteractor; //##Documentation //## @brief Tells if the displayed region should be shifted or rescaled if the render window is resized. bool m_KeepDisplayedRegion; protected: void PrintSelf(std::ostream &os, itk::Indent indent) const override; //##Documentation //## Data object containing the m_CurrentWorldPlaneGeometry defined above. PlaneGeometryData::Pointer m_CurrentWorldPlaneGeometryData; //##Documentation //## DataNode objects containing the m_CurrentWorldPlaneGeometryData defined above. DataNode::Pointer m_CurrentWorldPlaneGeometryNode; //##Documentation //## @brief test only unsigned long m_CurrentWorldPlaneGeometryTransformTime; std::string m_Name; double m_Bounds[6]; bool m_EmptyWorldGeometry; typedef std::set LODEnabledMappersType; /** Number of mappers which are visible and have level-of-detail * rendering enabled */ unsigned int m_NumberOfVisibleLODEnabledMappers; // Local Storage Handling for mappers protected: std::list m_RegisteredLocalStorageHandlers; bool m_ConstrainZoomingAndPanning; public: void RemoveAllLocalStorages(); void RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); void UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh); }; } // namespace mitk #endif /* BASERENDERER_H_HEADER_INCLUDED_C1CCA0F4 */ diff --git a/Modules/Core/include/mitkDataNode.h b/Modules/Core/include/mitkDataNode.h index 7199095d72..13f64e52b6 100644 --- a/Modules/Core/include/mitkDataNode.h +++ b/Modules/Core/include/mitkDataNode.h @@ -1,598 +1,597 @@ /*============================================================================ 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 DATATREENODE_H_HEADER_INCLUDED_C1E14338 #define DATATREENODE_H_HEADER_INCLUDED_C1E14338 #include "mitkBaseData.h" //#include "mitkMapper.h" #include "mitkDataInteractor.h" #include "mitkIdentifiable.h" #include "mitkIPropertyOwner.h" #include #include #include "mitkColorProperty.h" #include "mitkPropertyList.h" #include "mitkStringProperty.h" //#include "mitkMapper.h" #include "mitkGeometry3D.h" #include "mitkLevelWindow.h" #include #include class vtkLinearTransform; namespace mitk { class BaseRenderer; class Mapper; /** * \brief Class for nodes of the DataTree * * Contains the data (instance of BaseData), a list of mappers, which can * draw the data, a transform (vtkTransform) and a list of properties * (PropertyList). * \ingroup DataManagement * * \todo clean up all the GetProperty methods. There are too many different flavours... Can most probably be reduced * to * bool GetProperty(type&) * * \warning Change in semantics of SetProperty() since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). */ class MITKCORE_EXPORT DataNode : public itk::DataObject, public IPropertyOwner { public: typedef mitk::Geometry3D::Pointer Geometry3DPointer; typedef std::vector> MapperVector; typedef std::map MapOfPropertyLists; typedef std::vector PropertyListKeyNames; typedef std::set GroupTagList; /** * \brief Definition of an itk::Event that is invoked when * a DataInteractor is set on this DataNode. */ itkEventMacro(InteractorChangedEvent, itk::AnyEvent) mitkClassMacroItkParent(DataNode, itk::DataObject); itkFactorylessNewMacro(Self); - itkCloneMacro(Self); // IPropertyProvider BaseProperty::ConstPointer GetConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) const override; std::vector GetPropertyKeys(const std::string &contextName = "", bool includeDefaultContext = false) const override; std::vector GetPropertyContextNames() const override; // IPropertyOwner BaseProperty * GetNonConstProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = true) override; void SetProperty(const std::string &propertyKey, BaseProperty *property, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; void RemoveProperty(const std::string &propertyKey, const std::string &contextName = "", bool fallBackOnDefaultContext = false) override; mitk::Mapper *GetMapper(MapperSlotId id) const; /** * \brief Get the data object (instance of BaseData, e.g., an Image) * managed by this DataNode */ BaseData *GetData() const; /** * \brief Get the transformation applied prior to displaying the data as * a vtkTransform * \deprecated use GetData()->GetGeometry()->GetVtkTransform() instead */ vtkLinearTransform *GetVtkTransform(int t = 0) const; /** * \brief Set the data object (instance of BaseData, e.g., an Image) * managed by this DataNode * * Prior set properties are kept if previous data of the node already exists and has the same * type as the new data to be set. Otherwise, the default properties are used. * In case that previous data already exists, the property list of the data node is cleared * before setting new default properties. * * \warning the actor-mode of the vtkInteractor does not work any more, if the transform of the * data-tree-node is connected to the transform of the basedata via vtkTransform->SetInput. */ virtual void SetData(mitk::BaseData *baseData); /** * \brief Set the Interactor. */ virtual void SetDataInteractor(const DataInteractor::Pointer interactor); virtual DataInteractor::Pointer GetDataInteractor() const; mitk::DataNode &operator=(const DataNode &right); mitk::DataNode &operator=(BaseData *right); virtual void SetMapper(MapperSlotId id, mitk::Mapper *mapper); void UpdateOutputInformation() override; void SetRequestedRegionToLargestPossibleRegion() override; bool RequestedRegionIsOutsideOfTheBufferedRegion() override; bool VerifyRequestedRegion() override; void SetRequestedRegion(const itk::DataObject *data) override; void CopyInformation(const itk::DataObject *data) override; /** * \brief The "names" used for (renderer-specific) PropertyLists in GetPropertyList(string). * * All possible values for the "renderer" parameters of * the diverse GetProperty/List() methods. */ PropertyListKeyNames GetPropertyListNames() const; /** * \brief Set the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-value. * * \warning Change in semantics since Aug 25th 2006. Check your usage of this method if you do * more with properties than just call SetProperty( "key", new SomeProperty("value") ). * * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void SetProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Replace the property (instance of BaseProperty) with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent PropertyList). This is set-by-reference. * * If \a renderer is \a nullptr the property is set in the BaseRenderer-independent * PropertyList of this DataNode. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void ReplaceProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Add the property (instance of BaseProperty) if it does * not exist (or always if\a overwrite is\a true) * with key \a propertyKey in the PropertyList * of the \a renderer (if nullptr, use BaseRenderer-independent * PropertyList). This is set-by-value. * * For\a overwrite ==\a false the property is\em not changed * if it already exists. For\a overwrite ==\a true the method * is identical to SetProperty. * * \sa SetProperty * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ void AddProperty(const char *propertyKey, BaseProperty *property, const mitk::BaseRenderer *renderer = nullptr, bool overwrite = false); /** * \brief Get the PropertyList of the \a renderer. If \a renderer is \a * nullptr, the BaseRenderer-independent PropertyList of this DataNode * is returned. * \sa GetProperty * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::PropertyList *GetPropertyList(const mitk::BaseRenderer *renderer = nullptr) const; mitk::PropertyList *GetPropertyList(const std::string &rendererName) const; /** * \brief Add values from another PropertyList. * * Overwrites values in m_PropertyList only when possible (i.e. when types are compatible). * If you want to allow for object type changes (replacing a "visible":BoolProperty with "visible":IntProperty, * set the \param replace. * * \param replace true: if \param pList contains a property "visible" of type ColorProperty and our m_PropertyList * also has a "visible" property of a different type (e.g. BoolProperty), change the type, i.e. replace the objects * behind the pointer. * * \sa SetProperty * \sa ReplaceProperty * \sa m_PropertyList */ void ConcatenatePropertyList(PropertyList *pList, bool replace = false); /** * \brief Get the property (instance of BaseProperty) with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * * If \a fallBackOnDataProperties is true, the data property list is queried as a last resort. * * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ mitk::BaseProperty *GetProperty(const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr, bool fallBackOnDataProperties = true) const; /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(itk::SmartPointer &property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property.IsNotNull(); } /** * \brief Get the property of type T with key \a propertyKey from the PropertyList * of the \a renderer, if available there, otherwise use the BaseRenderer-independent PropertyList. * * If \a renderer is \a nullptr or the \a propertyKey cannot be found * in the PropertyList specific to \a renderer or is disabled there, the BaseRenderer-independent * PropertyList of this DataNode is queried. * \sa GetPropertyList * \sa m_PropertyList * \sa m_MapOfPropertyLists */ template bool GetProperty(T *&property, const char *propertyKey, const mitk::BaseRenderer *renderer = nullptr) const { property = dynamic_cast(GetProperty(propertyKey, renderer)); return property != nullptr; } /** * \brief Convenience access method for GenericProperty properties * (T being the type of the second parameter) * \return \a true property was found */ template bool GetPropertyValue(const char *propertyKey, T &value, const mitk::BaseRenderer *renderer = nullptr) const { GenericProperty *gp = dynamic_cast *>(GetProperty(propertyKey, renderer)); if (gp != nullptr) { value = gp->GetValue(); return true; } return false; } /// \brief Get a set of all group tags from this node's property list GroupTagList GetGroupTags() const; /** * \brief Convenience access method for bool properties (instances of * BoolProperty) * \return \a true property was found */ bool GetBoolProperty(const char *propertyKey, bool &boolValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for int properties (instances of * IntProperty) * \return \a true property was found */ bool GetIntProperty(const char *propertyKey, int &intValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for float properties (instances of * FloatProperty) * \return \a true property was found */ bool GetFloatProperty(const char *propertyKey, float &floatValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for double properties (instances of * DoubleProperty) * * If there is no DoubleProperty for the given\c propertyKey argument, the method * looks for a corresponding FloatProperty instance. * * \return \a true property was found */ bool GetDoubleProperty(const char *propertyKey, double &doubleValue, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for string properties (instances of * StringProperty) * \return \a true property was found */ bool GetStringProperty(const char *propertyKey, std::string &string, const mitk::BaseRenderer *renderer = nullptr) const; /** * \brief Convenience access method for color properties (instances of * ColorProperty) * \return \a true property was found */ bool GetColor(float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color") const; /** * \brief Convenience access method for level-window properties (instances of * LevelWindowProperty) * \return \a true property was found */ bool GetLevelWindow(mitk::LevelWindow &levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow") const; /** * \brief set the node as selected */ void SetSelected(bool selected, const mitk::BaseRenderer *renderer = nullptr); /** * \brief set the node as selected * \return \a true node is selected */ bool IsSelected(const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name") * \return \a true property was found */ bool GetName(std::string &nodeName, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "name") const { return GetStringProperty(propertyKey, nodeName, renderer); } /** * \brief Extra convenience access method for accessing the name of an object (instance of * StringProperty with property-key "name"). * * This method does not take the renderer specific * propertylists into account, because the name of an object should never be renderer specific. * \returns a std::string with the name of the object (content of "name" Property). * If there is no "name" Property, an empty string will be returned. */ virtual std::string GetName() const { mitk::StringProperty *sp = dynamic_cast(this->GetProperty("name")); if (sp == nullptr) return ""; return sp->GetValue(); } /** Value constant that is used indicate that node names are not set so far.*/ static std::string NO_NAME_VALUE() { return "No Name!"; } /** * \brief Extra convenience access method to set the name of an object. * * The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const char *name) { if (name == nullptr) return; this->SetProperty("name", StringProperty::New(name)); } /** * \brief Extra convenience access method to set the name of an object. * * The name will be stored in the non-renderer-specific PropertyList in a StringProperty named "name". */ virtual void SetName(const std::string name) { this->SetName(name.c_str()); } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty with property-key "visible") * \return \a true property was found * \sa IsVisible */ bool GetVisibility(bool &visible, const mitk::BaseRenderer *renderer, const char *propertyKey = "visible") const { return GetBoolProperty(propertyKey, visible, renderer); } /** * \brief Convenience access method for opacity properties (instances of * FloatProperty) * \return \a true property was found */ bool GetOpacity(float &opacity, const mitk::BaseRenderer *renderer, const char *propertyKey = "opacity") const; /** * \brief Convenience access method for boolean properties (instances * of BoolProperty). Return value is the value of the property. If the property is * not found, the value of \a defaultIsOn is returned. * * Thus, the return value has a different meaning than in the * GetBoolProperty method! * \sa GetBoolProperty */ bool IsOn(const char *propertyKey, const mitk::BaseRenderer *renderer, bool defaultIsOn = true) const { if (propertyKey == nullptr) return defaultIsOn; GetBoolProperty(propertyKey, defaultIsOn, renderer); return defaultIsOn; } /** * \brief Convenience access method for visibility properties (instances * of BoolProperty). Return value is the visibility. Default is * visible==true, i.e., true is returned even if the property (\a * propertyKey) is not found. * * Thus, the return value has a different meaning than in the * GetVisibility method! * \sa GetVisibility * \sa IsOn */ bool IsVisible(const mitk::BaseRenderer *renderer, const char *propertyKey = "visible", bool defaultIsOn = true) const { return IsOn(propertyKey, renderer, defaultIsOn); } /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const mitk::Color &color, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(float red, float green, float blue, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting color properties (instances of * ColorProperty) */ void SetColor(const float rgb[3], const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "color"); /** * \brief Convenience method for setting visibility properties (instances * of BoolProperty) * \param visible If set to true, the data will be rendered. If false, the render will skip this data. * \param renderer Specify a renderer if the visibility shall be specific to a renderer * \param propertykey Can be used to specify a user defined name of the visibility propery. */ void SetVisibility(bool visible, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "visible"); /** * \brief Convenience method for setting opacity properties (instances of * FloatProperty) */ void SetOpacity(float opacity, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "opacity"); /** * \brief Convenience method for setting level-window properties * (instances of LevelWindowProperty) */ void SetLevelWindow(mitk::LevelWindow levelWindow, const mitk::BaseRenderer *renderer = nullptr, const char *propertyKey = "levelwindow"); /** * \brief Convenience method for setting int properties (instances of * IntProperty) */ void SetIntProperty(const char *propertyKey, int intValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting boolean properties (instances of * BoolProperty) */ void SetBoolProperty(const char *propertyKey, bool boolValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting float properties (instances of * FloatProperty) */ void SetFloatProperty(const char *propertyKey, float floatValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting double properties (instances of * DoubleProperty) */ void SetDoubleProperty(const char *propertyKey, double doubleValue, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Convenience method for setting string properties (instances of * StringProperty) */ void SetStringProperty(const char *propertyKey, const char *string, const mitk::BaseRenderer *renderer = nullptr); /** * \brief Get the timestamp of the last change of the contents of this node or * the referenced BaseData. */ unsigned long GetMTime() const override; /** * \brief Get the timestamp of the last change of the reference to the * BaseData. */ unsigned long GetDataReferenceChangedTime() const { return m_DataReferenceChangedTime.GetMTime(); } protected: DataNode(); ~DataNode() override; /// Invoked when the property list was modified. Calls Modified() of the DataNode virtual void PropertyListModified(const itk::Object *caller, const itk::EventObject &event); /// \brief Mapper-slots mutable MapperVector m_Mappers; /** * \brief The data object (instance of BaseData, e.g., an Image) managed * by this DataNode */ BaseData::Pointer m_Data; /** * \brief BaseRenderer-independent PropertyList * * Properties herein can be overwritten specifically for each BaseRenderer * by the BaseRenderer-specific properties defined in m_MapOfPropertyLists. */ PropertyList::Pointer m_PropertyList; /// \brief Map associating each BaseRenderer with its own PropertyList mutable MapOfPropertyLists m_MapOfPropertyLists; DataInteractor::Pointer m_DataInteractor; /// \brief Timestamp of the last change of m_Data itk::TimeStamp m_DataReferenceChangedTime; unsigned long m_PropertyListModifiedObserverTag; }; MITKCORE_EXPORT std::istream &operator>>(std::istream &i, DataNode::Pointer &dtn); MITKCORE_EXPORT std::ostream &operator<<(std::ostream &o, DataNode::Pointer &dtn); } // namespace mitk #endif /* DATATREENODE_H_HEADER_INCLUDED_C1E14338 */ diff --git a/Modules/Core/include/mitkExtractSliceFilter.h b/Modules/Core/include/mitkExtractSliceFilter.h index d687c5dedb..e4b1ae2e3d 100644 --- a/Modules/Core/include/mitkExtractSliceFilter.h +++ b/Modules/Core/include/mitkExtractSliceFilter.h @@ -1,205 +1,208 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkExtractSliceFilter_h_Included #define mitkExtractSliceFilter_h_Included #include "MitkCoreExports.h" #include "mitkImageToImageFilter.h" #include #include #include #include #include #include #include namespace mitk { /** \brief ExtractSliceFilter extracts a 2D abitrary oriented slice from a 3D volume. The filter can reslice in all orthogonal planes such as sagittal, coronal and axial, and is also able to reslice an arbitrary oriented oblique plane. Curved planes are specified via an AbstractTransformGeometry as the input worldgeometry. Additionally the filter extracts the specified component of a multi-component input image. This is done only if the caller requests an mitk::Image output ('m_VtkOutputRequested' set to false). The default component to be extracted is '0'. The convenient workflow is: 1. Set an image as input. 2. Set the worldPlaneGeometry. This defines a grid where the slice is being extracted 3. And then start the pipeline. There are a few more properties that can be set to modify the behavior of the slicing. The properties are: - interpolation mode either Nearestneighbor, Linear or Cubic. - a transform this is a convenient way to adapt the reslice axis for the case that the image is transformed e.g. rotated. - time step the time step in a times volume. - the component to extract from a multi-component input image - vtkoutputrequested, to define whether an mitk::image should be initialized - resample by geometry whether the resampling grid corresponds to the specs of the worldgeometry or is directly derived from the input image By default the properties are set to: - interpolation mode Nearestneighbor. - a transform nullptr (No transform is set). - time step 0. - component 0. - resample by geometry false (Corresponds to input image). */ class MITKCORE_EXPORT ExtractSliceFilter : public ImageToImageFilter { public: mitkClassMacro(ExtractSliceFilter, ImageToImageFilter); itkFactorylessNewMacro(Self); itkCloneMacro(Self); - mitkNewMacro1Param(Self, vtkImageReslice *); + mitkNewMacro1Param(Self, vtkImageReslice *); /** \brief Set the axis where to reslice at.*/ void SetWorldGeometry(const PlaneGeometry *geometry) { - this->m_WorldGeometry = geometry; - this->Modified(); + if (geometry != m_WorldGeometry) + { + this->m_WorldGeometry = geometry; + this->Modified(); + } } /** \brief Set the time step in the 4D volume */ void SetTimeStep(unsigned int timestep) { m_TimeStep = timestep; } unsigned int GetTimeStep() { return m_TimeStep; } /** \brief Set the component of an image to be extracted */ void SetComponent(unsigned int component) { m_Component = component; } /** \brief Set a transform for the reslice axes. * This transform is needed if the image volume itself is transformed. (Effects the reslice axis) */ void SetResliceTransformByGeometry(const BaseGeometry *transform) { this->m_ResliceTransform = transform; } /** \brief Resampling grid corresponds to: false->image true->worldgeometry*/ void SetInPlaneResampleExtentByGeometry(bool inPlaneResampleExtentByGeometry) { this->m_InPlaneResampleExtentByGeometry = inPlaneResampleExtentByGeometry; } /** \brief Sets the output dimension of the slice*/ void SetOutputDimensionality(unsigned int dimension) { this->m_OutputDimension = dimension; } /** \brief Set the spacing in z direction manually. * Required if the outputDimension is > 2. */ void SetOutputSpacingZDirection(double zSpacing) { this->m_ZSpacing = zSpacing; } /** \brief Set the extent in pixel for direction z manualy. Required if the output dimension is > 2. */ void SetOutputExtentZDirection(int zMin, int zMax) { this->m_ZMin = zMin; this->m_ZMax = zMax; } /** \brief Get the bounding box of the slice [xMin, xMax, yMin, yMax, zMin, zMax] * The method uses the input of the filter to calculate the bounds. * It is recommended to use * GetClippedPlaneBounds(const BaseGeometry*, const PlaneGeometry*, double*) * if you are not sure about the input. */ bool GetClippedPlaneBounds(double bounds[6]); /** \brief Get the bounding box of the slice [xMin, xMax, yMin, yMax, zMin, zMax]*/ bool GetClippedPlaneBounds(const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds); /** \brief Get the spacing of the slice. returns mitk::ScalarType[2] */ mitk::ScalarType *GetOutputSpacing(); /** \brief Get Output as vtkImageData. * Note: * SetVtkOutputRequest(true) has to be called at least once before * GetVtkOutput(). Otherwise the output is empty for the first update step. */ vtkImageData *GetVtkOutput() { m_VtkOutputRequested = true; return m_Reslicer->GetOutput(); } /** Set VtkOutPutRequest to suppress the convertion of the image. * It is suggested to use this with GetVtkOutput(). * Note: * SetVtkOutputRequest(true) has to be called at least once before * GetVtkOutput(). Otherwise the output is empty for the first update step. */ void SetVtkOutputRequest(bool isRequested) { m_VtkOutputRequested = isRequested; } /** \brief Get the reslices axis matrix. * Note: the axis are recalculated when calling SetResliceTransformByGeometry. */ vtkMatrix4x4 *GetResliceAxes() { return this->m_Reslicer->GetResliceAxes(); } void SetBackgroundLevel(double backgroundLevel) { m_BackgroundLevel = backgroundLevel; } enum ResliceInterpolation { RESLICE_NEAREST = 0, RESLICE_LINEAR = 1, RESLICE_CUBIC = 3 }; void SetInterpolationMode(ExtractSliceFilter::ResliceInterpolation interpolation) { this->m_InterpolationMode = interpolation; } protected: ExtractSliceFilter(vtkImageReslice *reslicer = nullptr); ~ExtractSliceFilter() override; void GenerateData() override; void GenerateOutputInformation() override; void GenerateInputRequestedRegion() override; - const PlaneGeometry *m_WorldGeometry; + PlaneGeometry::ConstPointer m_WorldGeometry; vtkSmartPointer m_Reslicer; unsigned int m_TimeStep; unsigned int m_OutputDimension; double m_ZSpacing; int m_ZMin; int m_ZMax; ResliceInterpolation m_InterpolationMode; bool m_InPlaneResampleExtentByGeometry; // Resampling grid corresponds to: false->image true->worldgeometry mitk::ScalarType *m_OutPutSpacing; bool m_VtkOutputRequested; double m_BackgroundLevel; unsigned int m_Component; private: BaseGeometry::ConstPointer m_ResliceTransform; /* Axis vectors of the relevant geometry. Set in GenerateOutputInformation() and also used in GenerateData().*/ Vector3D m_Right, m_Bottom; /* Bounds of the relevant plane. Set in GenerateOutputInformation() and also used in GenerateData().*/ int m_XMin, m_XMax, m_YMin, m_YMax; }; } #endif // mitkExtractSliceFilter_h_Included diff --git a/Modules/Core/include/mitkIdentifiable.h b/Modules/Core/include/mitkIdentifiable.h index 1d6412cfc7..9c2cef821f 100644 --- a/Modules/Core/include/mitkIdentifiable.h +++ b/Modules/Core/include/mitkIdentifiable.h @@ -1,60 +1,77 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkIdentifiable_h #define mitkIdentifiable_h #include #include namespace mitk { /** * \brief Base class of identifiable objects. * - * If you want to change the unique ID after creation time, you can use + * Offers an interface to query a UID for the instance. Can be inherited + * by other classes to provide this capability.\n\n + * What does the UID stand for/what is its scope?\n + * - It is unique in its creation, but it is not a content specific unique ID. Therfore: + * - A class instance, associated with a UID, may change its values over its lifetime, + but still have the same UID. + - If a class instance gets persisted and loaded multiple times, then their could + be several instances with the same UID. + - UIDs are therefore more simelar to git paths than to git hashes. They identify something, but the state of + something can change (stream of commits). The difference of the UID compared to using e.g. plain instance + pointers to identify an object is that UIDs allow the feature of serialization as they abstract from the + memory location of the current runtime environment. + - It is up to the application that builds upon that feature to ensure appropriate usage within + the application scope. + * @remark It is not a feature of mitk::Identifiable per se to be persistable. It depends on classes that derive + * from mitk::Identifiable, if and how they implement a persistence mechanism for their MITK UID. + * @remark If you want to change the unique ID after creation time, you can use * the mitk::UIDManipulator class. The reationale behind this pattern is * to ensure that you think twice before doing this. It is intended to be * used by data readers if necessary at all. */ class MITKCORE_EXPORT Identifiable { public: using UIDType = std::string; Identifiable(); explicit Identifiable(const UIDType &uid); Identifiable(const Identifiable &) = delete; Identifiable(Identifiable &&) noexcept; virtual ~Identifiable(); Identifiable & operator =(const Identifiable &) = delete; Identifiable & operator =(Identifiable &&other) noexcept; /** * \brief Get unique ID of an object. * * \return Empty string if an object has no unique ID. */ - UIDType GetUID() const; + virtual UIDType GetUID() const; + + protected: + virtual void SetUID(const UIDType& uid); private: friend class UIDManipulator; - void SetUID(const UIDType &uid); - struct Impl; Impl *m_Impl; }; } #endif diff --git a/Modules/Core/include/mitkItkImageIO.h b/Modules/Core/include/mitkItkImageIO.h index cefc222130..861243eebc 100644 --- a/Modules/Core/include/mitkItkImageIO.h +++ b/Modules/Core/include/mitkItkImageIO.h @@ -1,79 +1,82 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKITKFILEIO_H #define MITKITKFILEIO_H #include "mitkAbstractFileIO.h" #include namespace mitk { /** * This class wraps ITK image IO objects as mitk::IFileReader and * mitk::IFileWriter objects. * * Instantiating this class with a given itk::ImageIOBase instance * will register corresponding MITK reader/writer services for that * ITK ImageIO object. + * For all ITK ImageIOs that support the serialization of MetaData + * (e.g. nrrd or mhd) the ItkImageIO ensures the serialization + * of Identification UID. */ class MITKCORE_EXPORT ItkImageIO : public AbstractFileIO { public: ItkImageIO(itk::ImageIOBase::Pointer imageIO); ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; protected: virtual std::vector FixUpImageIOExtensions(const std::string &imageIOName); virtual void FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType); // Fills the m_DefaultMetaDataKeys vector with default values virtual void InitializeDefaultMetaDataKeys(); // -------------- AbstractFileReader ------------- std::vector> DoRead() override; private: ItkImageIO(const ItkImageIO &other); ItkImageIO *IOClone() const override; itk::ImageIOBase::Pointer m_ImageIO; std::vector m_DefaultMetaDataKeys; }; /**Helper function that converts the content of a meta data into a time point vector. * If MetaData is not valid or cannot be converted an empty vector is returned.*/ MITKCORE_EXPORT std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data); /**Helper function that converts the time points of a passed time geometry to a time point list and stores it in a itk::MetaDataObject. Use ConvertMetaDataObjectToTimePointList() to convert it back to a time point list.*/ MITKCORE_EXPORT itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry); } // namespace mitk #endif /* MITKITKFILEIO_H */ diff --git a/Modules/Core/include/mitkNodePredicateSubGeometry.h b/Modules/Core/include/mitkNodePredicateSubGeometry.h new file mode 100644 index 0000000000..dbb6dcc602 --- /dev/null +++ b/Modules/Core/include/mitkNodePredicateSubGeometry.h @@ -0,0 +1,68 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef MITKNODEPREDICATESUBGEOMETRY_H_HEADER_INCLUDED_ +#define MITKNODEPREDICATESUBGEOMETRY_H_HEADER_INCLUDED_ + +#include "mitkNodePredicateBase.h" +#include "mitkBaseGeometry.h" +#include "mitkTimeGeometry.h" + +namespace mitk +{ + class BaseData; + + /**Documentation + @brief Predicate that evaluates if the given DataNode's data object + has a geometry that is a sub geomety of the reference geometry. + Sub geometry means that both geometries have the same voxel grid (same spacing, same axes, + orgin is on voxel grid), but the bounding box of the checked geometry is contained or equal + to the bounding box of the reference geometry.\n + One can either check the whole time geometry of + the data node by defining a referenc time geometry or check against one given2 + reference base geometry. If the predicate should check against a base geometry, + you can specify the timepoint of the data's time geometry that should be checked. + If no timepoint is defined the predicate will evaluate the data geometry in + the first timestep. + Evaluates to "false" for unsupported or undefined data objects/geometries. + @ingroup DataStorage */ + class MITKCORE_EXPORT NodePredicateSubGeometry : public NodePredicateBase + { + public: + mitkClassMacro(NodePredicateSubGeometry, NodePredicateBase); + mitkNewMacro1Param(NodePredicateSubGeometry, const BaseGeometry*); + mitkNewMacro2Param(NodePredicateSubGeometry, const BaseGeometry*, TimePointType); + + itkSetMacro(CheckPrecision, mitk::ScalarType); + itkGetMacro(CheckPrecision, mitk::ScalarType); + + ~NodePredicateSubGeometry() override; + + bool CheckNode(const mitk::DataNode *node) const override; + + protected: + + /**Constructor that is used configures the predicate to check the reference geometry against the first data timepoint.*/ + NodePredicateSubGeometry(const BaseGeometry* refGeometry); + /**Constructor allows to define the timepoint that should be evaluated against the reference.*/ + NodePredicateSubGeometry(const BaseGeometry* refGeometry, TimePointType relevantTimePoint); + + BaseGeometry::ConstPointer m_RefGeometry; + TimePointType m_TimePoint; + /**Indicates if m_TimePoint should be regarded or always the first timestep should be used.*/ + bool m_UseTimePoint; + /**Precision that should be used for the equal checks.*/ + mitk::ScalarType m_CheckPrecision; + }; +} // namespace mitk + +#endif diff --git a/Modules/Core/include/mitkUIDGenerator.h b/Modules/Core/include/mitkUIDGenerator.h index f05a241f2c..0b7931f039 100644 --- a/Modules/Core/include/mitkUIDGenerator.h +++ b/Modules/Core/include/mitkUIDGenerator.h @@ -1,49 +1,44 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITK_UID_GENERATOR_INDCLUDED_FASAWE #define MITK_UID_GENERATOR_INDCLUDED_FASAWE #include -#include -#include #include namespace mitk { /*! \brief Generated unique IDs - Creates unique IDs from a given prefix, the current date/time and a random part. Be aware that, a length of one or - two might not be sufficient. + Creates unique IDs. - The prefix is given to the constructor, together with the desired - length of the random part. - - The current implementation uses the time in seconds in combination with an a random part. + The current implementation uses the UUID specification (https://www.ietf.org/rfc/rfc4122.txt) and + random generator. + One may define a prefix for the UID string. But it is not needed to guarantee uniquness. It is + just a human readable addition, e.g. to see for which purpose the UID was generated. */ class MITKCORE_EXPORT UIDGenerator { public: - UIDGenerator(const char *prefix = "UID_", unsigned int lengthOfRandomPart = 8); + explicit UIDGenerator(const char * prefix = ""); /** @return Returns a unique ID as string. You will get another unique ID each time you call GetUID. */ std::string GetUID(); private: std::string m_Prefix; - unsigned int m_LengthOfRandomPart; - std::uniform_int_distribution m_Distribution; }; } // namespace mitk #endif diff --git a/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp b/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp index 5bd97fb640..ec1081234d 100644 --- a/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp +++ b/Modules/Core/src/Algorithms/mitkExtractSliceFilter.cpp @@ -1,514 +1,504 @@ /*============================================================================ 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 "mitkExtractSliceFilter.h" #include #include #include #include #include #include #include mitk::ExtractSliceFilter::ExtractSliceFilter(vtkImageReslice *reslicer): m_XMin(0), m_XMax(0), m_YMin(0), m_YMax(0) { if (reslicer == nullptr) { m_Reslicer = vtkSmartPointer::New(); } else { m_Reslicer = reslicer; } m_TimeStep = 0; m_Reslicer->ReleaseDataFlagOn(); m_InterpolationMode = ExtractSliceFilter::RESLICE_NEAREST; m_ResliceTransform = nullptr; m_InPlaneResampleExtentByGeometry = false; m_OutPutSpacing = new mitk::ScalarType[2]; m_OutputDimension = 2; m_ZSpacing = 1.0; m_ZMin = 0; m_ZMax = 0; m_VtkOutputRequested = false; m_BackgroundLevel = -32768.0; m_Component = 0; } mitk::ExtractSliceFilter::~ExtractSliceFilter() { m_ResliceTransform = nullptr; m_WorldGeometry = nullptr; delete[] m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateOutputInformation() { Image::ConstPointer input = this->GetInput(); if (input.IsNull()) { return; } if (nullptr == m_WorldGeometry) { return; } Vector3D right, bottom; double widthInMM, heightInMM; Vector2D extent; - // set the geometry from current worldgeometry for the reusultimage + // set the geometry from current worldgeometry for the resultimage // this is needed that the image has the correct mitk geometry // the sliceGeometry is the Geometry of the result slice PlaneGeometry::Pointer sliceGeometry = m_WorldGeometry->Clone(); sliceGeometry->GetIndexToWorldTransform()->SetMatrix(m_WorldGeometry->GetIndexToWorldTransform()->GetMatrix()); // the origin of the worldGeometry is transformed to center based coordinates to be an imageGeometry Point3D sliceOrigin = sliceGeometry->GetOrigin(); auto abstractGeometry = - dynamic_cast(m_WorldGeometry); - - auto planeGeometry = dynamic_cast(m_WorldGeometry); + dynamic_cast(m_WorldGeometry.GetPointer()); if (abstractGeometry != nullptr) { extent[0] = abstractGeometry->GetParametricExtent(0); extent[1] = abstractGeometry->GetParametricExtent(1); widthInMM = abstractGeometry->GetParametricExtentInMM(0); heightInMM = abstractGeometry->GetParametricExtentInMM(1); right = abstractGeometry->GetPlane()->GetAxisVector(0); bottom = abstractGeometry->GetPlane()->GetAxisVector(1); } else { - if (planeGeometry != nullptr) - { - // if the worldGeomatry is a PlaneGeometry everything is straight forward - right = planeGeometry->GetAxisVector(0); - bottom = planeGeometry->GetAxisVector(1); + // if the worldGeomatry is a PlaneGeometry everything is straight forward + right = m_WorldGeometry->GetAxisVector(0); + bottom = m_WorldGeometry->GetAxisVector(1); - if (m_InPlaneResampleExtentByGeometry) - { - // Resampling grid corresponds to the current world geometry. This - // means that the spacing of the output 2D image depends on the - // currently selected world geometry, and *not* on the image itself. - extent[0] = m_WorldGeometry->GetExtent(0); - extent[1] = m_WorldGeometry->GetExtent(1); - } - else - { - const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); - if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() <= 0)) - { - itkWarningMacro(<< "Error reading input image TimeGeometry."); - return; - } - - // Resampling grid corresponds to the input geometry. This means that - // the spacing of the output 2D image is directly derived from the - // associated input image, regardless of the currently selected world - // geometry. - Vector3D rightInIndex, bottomInIndex; - inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->WorldToIndex(right, rightInIndex); - inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->WorldToIndex(bottom, bottomInIndex); - extent[0] = rightInIndex.GetNorm(); - extent[1] = bottomInIndex.GetNorm(); - } - - // Get the extent of the current world geometry and calculate resampling - // spacing therefrom. - widthInMM = m_WorldGeometry->GetExtentInMM(0); - heightInMM = m_WorldGeometry->GetExtentInMM(1); + if (m_InPlaneResampleExtentByGeometry) + { + // Resampling grid corresponds to the current world geometry. This + // means that the spacing of the output 2D image depends on the + // currently selected world geometry, and *not* on the image itself. + extent[0] = m_WorldGeometry->GetExtent(0); + extent[1] = m_WorldGeometry->GetExtent(1); } else { - mitkThrow()<<"mitk::ExtractSliceFilter: No fitting geometry for reslice axis!"; - return; + const TimeGeometry *inputTimeGeometry = input->GetTimeGeometry(); + if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() <= 0)) + { + itkWarningMacro(<< "Error reading input image TimeGeometry."); + return; + } + + // Resampling grid corresponds to the input geometry. This means that + // the spacing of the output 2D image is directly derived from the + // associated input image, regardless of the currently selected world + // geometry. + Vector3D rightInIndex, bottomInIndex; + inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->WorldToIndex(right, rightInIndex); + inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->WorldToIndex(bottom, bottomInIndex); + extent[0] = rightInIndex.GetNorm(); + extent[1] = bottomInIndex.GetNorm(); } + + // Get the extent of the current world geometry and calculate resampling + // spacing therefrom. + widthInMM = m_WorldGeometry->GetExtentInMM(0); + heightInMM = m_WorldGeometry->GetExtentInMM(1); } right.Normalize(); bottom.Normalize(); m_OutPutSpacing[0] = widthInMM / extent[0]; m_OutPutSpacing[1] = heightInMM / extent[1]; /*========== BEGIN setup extent of the slice ==========*/ int xMin, xMax, yMin, yMax; xMin = yMin = 0; xMax = static_cast(extent[0]); yMax = static_cast(extent[1]); if (m_WorldGeometry->GetReferenceGeometry()) { double sliceBounds[6]; for (auto &sliceBound : sliceBounds) { sliceBound = 0.0; } - if (this->GetClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), planeGeometry, sliceBounds)) + if (this->GetClippedPlaneBounds(m_WorldGeometry->GetReferenceGeometry(), m_WorldGeometry, sliceBounds)) { // Calculate output extent (integer values) xMin = static_cast(sliceBounds[0] / m_OutPutSpacing[0] + 0.5); xMax = static_cast(sliceBounds[1] / m_OutPutSpacing[0] + 0.5); yMin = static_cast(sliceBounds[2] / m_OutPutSpacing[1] + 0.5); yMax = static_cast(sliceBounds[3] / m_OutPutSpacing[1] + 0.5); } // ELSE we use the default values } sliceOrigin += right * (m_OutPutSpacing[0] * 0.5); sliceOrigin += bottom * (m_OutPutSpacing[1] * 0.5); // a worldGeometry is no imageGeometry, thus it is manually set to true sliceGeometry->ImageGeometryOn(); /*At this point we have to adjust the geometry because the origin isn't correct. The wrong origin is related to the rotation of the current world geometry plane. This causes errors on transferring world to index coordinates. We just shift the origin in each direction about the amount of the expanding (needed while rotating the plane). */ Vector3D axis0 = sliceGeometry->GetAxisVector(0); Vector3D axis1 = sliceGeometry->GetAxisVector(1); axis0.Normalize(); axis1.Normalize(); // adapt the origin. Note that for orthogonal planes the minima are '0' and thus the origin stays the same. sliceOrigin += (axis0 * (xMin * m_OutPutSpacing[0])) + (axis1 * (yMin * m_OutPutSpacing[1])); sliceGeometry->SetOrigin(sliceOrigin); /*the bounds as well as the extent of the worldGeometry are not adapted correctly during crosshair rotation. This is only a quick fix and has to be evaluated. The new bounds are set via the max values of the calculated slice extent. It will look like [ 0, x, 0, y, 0, 1]. */ mitk::BoundingBox::BoundsArrayType boundsCopy; boundsCopy[0] = boundsCopy[2] = boundsCopy[4] = 0; boundsCopy[5] = 1; - boundsCopy[1] = xMax - xMin; - boundsCopy[3] = yMax - yMin; + boundsCopy[1] = std::max(xMax - xMin, 1); + boundsCopy[3] = std::max(yMax - yMin, 1); sliceGeometry->SetBounds(boundsCopy); sliceGeometry->Modified(); Image::Pointer output = this->GetOutput(); output->Initialize(input->GetPixelType(), 2, *sliceGeometry); m_XMin = xMin; m_XMax = xMax; m_YMin = yMin; m_YMax = yMax; m_Right = right; m_Bottom = bottom; } void mitk::ExtractSliceFilter::GenerateInputRequestedRegion() { // As we want all pixel information fo the image in our plane, the requested region // is set to the largest possible region in the image. // This is needed because an oblique plane has a larger extent then the image // and the in pipeline it is checked via PropagateResquestedRegion(). But the // extent of the slice is actually fitting because it is oblique within the image. ImageToImageFilter::InputImagePointer input = this->GetInput(); input->SetRequestedRegionToLargestPossibleRegion(); } mitk::ScalarType *mitk::ExtractSliceFilter::GetOutputSpacing() { return m_OutPutSpacing; } void mitk::ExtractSliceFilter::GenerateData() { mitk::Image *input = this->GetInput(); if (!input) { MITK_ERROR << "mitk::ExtractSliceFilter: No input image available. Please set the input!" << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No input image available. Please set the input!"); return; } if (!m_WorldGeometry) { MITK_ERROR << "mitk::ExtractSliceFilter: No Geometry for reslicing available." << std::endl; itkExceptionMacro("mitk::ExtractSliceFilter: No Geometry for reslicing available."); return; } const TimeGeometry *inputTimeGeometry = this->GetInput()->GetTimeGeometry(); if ((inputTimeGeometry == nullptr) || (inputTimeGeometry->CountTimeSteps() <= 0)) { itkWarningMacro(<< "Error reading input image TimeGeometry."); return; } // is it a valid timeStep? if (inputTimeGeometry->IsValidTimeStep(m_TimeStep) == false) { itkWarningMacro(<< "This is not a valid timestep: " << m_TimeStep); return; } // check if there is something to display. if (!input->IsVolumeSet(m_TimeStep)) { itkWarningMacro(<< "No volume data existent at given timestep " << m_TimeStep); return; } /*================#BEGIN setup vtkImageReslice properties================*/ Point3D origin; Vector3D normal; - const auto *planeGeometry = dynamic_cast(m_WorldGeometry); + const auto *planeGeometry = dynamic_cast(m_WorldGeometry.GetPointer()); // Code for curved planes, mostly taken 1:1 from imageVtkMapper2D and not tested yet. // Do we have an AbstractTransformGeometry? // This is the case for AbstractTransformGeometry's (e.g. a ThinPlateSplineCurvedGeometry ) const auto *abstractGeometry = - dynamic_cast(m_WorldGeometry); + dynamic_cast(m_WorldGeometry.GetPointer()); if (abstractGeometry != nullptr) { m_ResliceTransform = abstractGeometry; origin = abstractGeometry->GetPlane()->GetOrigin(); normal = abstractGeometry->GetPlane()->GetNormal(); normal.Normalize(); // Use a combination of the InputGeometry *and* the possible non-rigid // AbstractTransformGeometry for reslicing the 3D Image vtkSmartPointer composedResliceTransform = vtkSmartPointer::New(); composedResliceTransform->Identity(); composedResliceTransform->Concatenate( inputTimeGeometry->GetGeometryForTimeStep(m_TimeStep)->GetVtkTransform()->GetLinearInverse()); composedResliceTransform->Concatenate(abstractGeometry->GetVtkAbstractTransform()); m_Reslicer->SetResliceTransform(composedResliceTransform); // Set background level to BLACK instead of translucent, to avoid // boundary artifacts (see PlaneGeometryDataVtkMapper3D) // Note: Backgroundlevel was hardcoded before to -1023 m_Reslicer->SetBackgroundLevel(m_BackgroundLevel); } else { if (planeGeometry != nullptr) { // if the worldGeomatry is a PlaneGeometry everything is straight forward origin = planeGeometry->GetOrigin(); normal = planeGeometry->GetNormal(); normal.Normalize(); /* * Transform the origin to center based coordinates. * Note: * This is needed besause vtk's origin is center based too (!!!) ( see 'The VTK book' page 88 ) * and the worldGeometry surrouding the image is no imageGeometry. So the worldGeometry * has its origin at the corner of the voxel and needs to be transformed. */ origin += m_Right * (m_OutPutSpacing[0] * 0.5); origin += m_Bottom * (m_OutPutSpacing[1] * 0.5); // set the tranform for reslicing. // Use inverse transform of the input geometry for reslicing the 3D image. // This is needed if the image volume already transformed if (m_ResliceTransform.IsNotNull()) m_Reslicer->SetResliceTransform(m_ResliceTransform->GetVtkTransform()->GetLinearInverse()); // Set background level to TRANSLUCENT (see PlaneGeometryDataVtkMapper3D), // else the background of the image turns out gray // Note: Backgroundlevel was hardcoded to -32768 m_Reslicer->SetBackgroundLevel(m_BackgroundLevel); } else { itkExceptionMacro("mitk::ExtractSliceFilter: No fitting geometry for reslice axis!"); return; } } if (m_ResliceTransform.IsNotNull()) { // if the resliceTransform is set the reslice axis are recalculated. // Thus the geometry information is not fitting. Therefor a unitSpacingFilter // is used to set up a global spacing of 1 and compensate the transform. vtkSmartPointer unitSpacingImageFilter = vtkSmartPointer::New(); unitSpacingImageFilter->ReleaseDataFlagOn(); unitSpacingImageFilter->SetOutputSpacing(1.0, 1.0, 1.0); unitSpacingImageFilter->SetInputData(input->GetVtkImageData(m_TimeStep)); m_Reslicer->SetInputConnection(unitSpacingImageFilter->GetOutputPort()); } else { // if no transform is set the image can be used directly m_Reslicer->SetInputData(input->GetVtkImageData(m_TimeStep)); } /*setup the plane where vktImageReslice extracts the slice*/ // ResliceAxesOrigin is the anchor point of the plane double originInVtk[3]; itk2vtk(origin, originInVtk); m_Reslicer->SetResliceAxesOrigin(originInVtk); // the cosines define the plane: x and y are the direction vectors, n is the planes normal // this specifies a matrix 3x3 // x1 y1 n1 // x2 y2 n2 // x3 y3 n3 double cosines[9]; vnl2vtk(m_Right.GetVnlVector(), cosines); // x vnl2vtk(m_Bottom.GetVnlVector(), cosines + 3); // y vnl2vtk(normal.GetVnlVector(), cosines + 6); // n m_Reslicer->SetResliceAxesDirectionCosines(cosines); // we only have one slice, not a volume m_Reslicer->SetOutputDimensionality(m_OutputDimension); // set the interpolation mode for slicing switch (this->m_InterpolationMode) { case RESLICE_NEAREST: m_Reslicer->SetInterpolationModeToNearestNeighbor(); break; case RESLICE_LINEAR: m_Reslicer->SetInterpolationModeToLinear(); break; case RESLICE_CUBIC: m_Reslicer->SetInterpolationModeToCubic(); break; default: // the default interpolation used by mitk m_Reslicer->SetInterpolationModeToNearestNeighbor(); } /*========== BEGIN setup extent of the slice ==========*/ // Set the output extents! First included pixel index and last included pixel index // xMax and yMax are one after the last pixel. so they have to be decremented by 1. // In case we have a 2D image, xMax or yMax might be 0. in this case, do not decrement, but take 0. m_Reslicer->SetOutputExtent(m_XMin, std::max(0, m_XMax - 1), m_YMin, std::max(0, m_YMax - 1), m_ZMin, m_ZMax); /*========== END setup extent of the slice ==========*/ m_Reslicer->SetOutputOrigin(0.0, 0.0, 0.0); m_Reslicer->SetOutputSpacing(m_OutPutSpacing[0], m_OutPutSpacing[1], m_ZSpacing); // TODO check the following lines, they are responsible whether vtk error outputs appear or not m_Reslicer->UpdateWholeExtent(); // this produces a bad allocation error for 2D images // m_Reslicer->GetOutput()->UpdateInformation(); // m_Reslicer->GetOutput()->SetUpdateExtentToWholeExtent(); // start the pipeline m_Reslicer->Update(); /*================ #END setup vtkImageReslice properties================*/ if (m_VtkOutputRequested) { // no conversion to mitk // no mitk geometry will be set, as the output is vtkImageData only!!! // no image component will be extracted, as the caller might need the whole multi-component image as vtk output return; } else { auto reslicedImage = vtkSmartPointer::New(); reslicedImage = m_Reslicer->GetOutput(); if (nullptr == reslicedImage) { itkWarningMacro(<< "Reslicer returned empty image"); return; } /*================ #BEGIN Extract component from image slice ================*/ int numberOfScalarComponent = reslicedImage->GetNumberOfScalarComponents(); if (numberOfScalarComponent > 1 && static_cast(numberOfScalarComponent) >= m_Component) { // image has more than one component, extract the correct component information with the given 'component' parameter auto vectorComponentExtractor = vtkSmartPointer::New(); vectorComponentExtractor->SetInputData(reslicedImage); vectorComponentExtractor->SetComponents(m_Component); vectorComponentExtractor->Update(); reslicedImage = vectorComponentExtractor->GetOutput(); } /*================ #END Extract component from image slice ================*/ /*================ #BEGIN Convert the slice to an mitk::Image ================*/ mitk::Image::Pointer resultImage = GetOutput(); /*Temporary store the geometry that is already correct (set in GeneratOutputInformation()) but will be reset due to initialize.*/ mitk::BaseGeometry::Pointer resultGeometry = resultImage->GetGeometry(); // initialize resultimage with the specs of the vtkImageData object returned from vtkImageReslice if (reslicedImage->GetDataDimension() == 1) { // If original image was 2D, the slice might have an y extent of 0. // Still i want to ensure here that Image is 2D resultImage->Initialize(reslicedImage, 1, -1, -1, 1); } else { resultImage->Initialize(reslicedImage); } // transfer the voxel data resultImage->SetVolume(reslicedImage->GetScalarPointer()); /*================ #END Convert the slice to an mitk::Image ================*/ resultImage->SetGeometry(resultGeometry); } } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(double bounds[6]) { if (!m_WorldGeometry || !this->GetInput()) return false; return this->GetClippedPlaneBounds( - m_WorldGeometry->GetReferenceGeometry(), dynamic_cast(m_WorldGeometry), bounds); + m_WorldGeometry->GetReferenceGeometry(), m_WorldGeometry, bounds); } bool mitk::ExtractSliceFilter::GetClippedPlaneBounds(const BaseGeometry *boundingGeometry, const PlaneGeometry *planeGeometry, double *bounds) { bool b = mitk::PlaneClipping::CalculateClippedPlaneBounds(boundingGeometry, planeGeometry, bounds); return b; } diff --git a/Modules/Core/src/Algorithms/mitkUIDGenerator.cpp b/Modules/Core/src/Algorithms/mitkUIDGenerator.cpp index a0e5158bc6..8cf27eacd2 100644 --- a/Modules/Core/src/Algorithms/mitkUIDGenerator.cpp +++ b/Modules/Core/src/Algorithms/mitkUIDGenerator.cpp @@ -1,55 +1,42 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#include #include -#include #include -mitk::UIDGenerator::UIDGenerator(const char *prefix, unsigned int lengthOfRandomPart) - : m_Prefix(prefix), - m_LengthOfRandomPart(lengthOfRandomPart), - m_Distribution(std::uniform_int_distribution(0, std::numeric_limits::max())) +#include + +#include +#include +#include + +boost::uuids::random_generator uuidGen; +std::mutex uuidGen_mutex; + +mitk::UIDGenerator::UIDGenerator(const char *prefix) + : m_Prefix(prefix) { } std::string mitk::UIDGenerator::GetUID() { std::ostringstream s; - s << m_Prefix; - auto time = std::time(nullptr); - auto tm = std::localtime(&time); - - s << std::put_time(tm, "%Y%m%d%H%M%S"); - - std::ostringstream rs; - - static std::random_device rd; // Will be used to obtain a seed for the random number engine - static std::mt19937 generator(rd()); // Standard mersenne_twister_engine seeded with rd() - while (rs.str().length() < m_LengthOfRandomPart) { - rs << m_Distribution(generator); + std::lock_guard guard(uuidGen_mutex); + auto uuid = uuidGen(); + s << m_Prefix << uuid; } - auto randomString = rs.str(); - - if (randomString.length() > m_LengthOfRandomPart) - { - randomString = randomString.substr(randomString.length() - m_LengthOfRandomPart); - } - - s << randomString; - return s.str(); } diff --git a/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp b/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp index c7b740b6b6..e593a15eca 100644 --- a/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkArbitraryTimeGeometry.cpp @@ -1,294 +1,307 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include mitk::ArbitraryTimeGeometry::ArbitraryTimeGeometry() = default; mitk::ArbitraryTimeGeometry::~ArbitraryTimeGeometry() = default; void mitk::ArbitraryTimeGeometry::Initialize() { this->ClearAllGeometries(); Geometry3D::Pointer geo = Geometry3D::New(); geo->Initialize(); this->AppendNewTimeStep(geo, 0, 1); Update(); } mitk::TimeStepType mitk::ArbitraryTimeGeometry::CountTimeSteps() const { return static_cast(m_GeometryVector.size()); } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMinimumTimePoint() const { return m_MinimumTimePoints.empty() ? 0.0 : m_MinimumTimePoints.front(); } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMaximumTimePoint() const { TimePointType result = 0; if ( !m_MaximumTimePoints.empty() ) { result = m_MaximumTimePoints.back(); } return result; } mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMinimumTimePoint( TimeStepType step ) const { TimePointType result = GetMinimumTimePoint(); if (step > 0 && step <= m_MaximumTimePoints.size()) { result = m_MaximumTimePoints[step - 1]; } return result; }; mitk::TimePointType mitk::ArbitraryTimeGeometry::GetMaximumTimePoint( TimeStepType step ) const { TimePointType result = 0; if (step < m_MaximumTimePoints.size()) { result = m_MaximumTimePoints[step]; } return result; }; mitk::TimeBounds mitk::ArbitraryTimeGeometry::GetTimeBounds() const { TimeBounds bounds; bounds[0] = this->GetMinimumTimePoint(); bounds[1] = this->GetMaximumTimePoint(); return bounds; } mitk::TimeBounds mitk::ArbitraryTimeGeometry::GetTimeBounds(TimeStepType step) const { TimeBounds bounds; bounds[0] = this->GetMinimumTimePoint( step ); bounds[1] = this->GetMaximumTimePoint( step ); return bounds; } bool mitk::ArbitraryTimeGeometry::IsValidTimePoint(TimePointType timePoint) const { - return this->GetMinimumTimePoint() <= timePoint && timePoint < this->GetMaximumTimePoint(); + return this->GetMinimumTimePoint() <= timePoint && + (timePoint < this->GetMaximumTimePoint() || (this->HasCollapsedFinalTimeStep() && timePoint <= this->GetMaximumTimePoint())); } bool mitk::ArbitraryTimeGeometry::IsValidTimeStep(TimeStepType timeStep) const { return timeStep < this->CountTimeSteps(); } mitk::TimePointType mitk::ArbitraryTimeGeometry::TimeStepToTimePoint( TimeStepType timeStep ) const { TimePointType result = 0.0; if (timeStep < m_MinimumTimePoints.size() ) { result = m_MinimumTimePoints[timeStep]; } return result; } mitk::TimeStepType mitk::ArbitraryTimeGeometry::TimePointToTimeStep(TimePointType timePoint) const { mitk::TimeStepType result = 0; if (timePoint >= GetMinimumTimePoint()) { for (auto pos = m_MaximumTimePoints.cbegin(); pos != m_MaximumTimePoints.cend(); ++pos) { - if (timePoint < *pos) + if (timePoint < *pos || (pos==std::prev(m_MaximumTimePoints.cend()) && timePoint <= *pos && this->HasCollapsedFinalTimeStep())) { break; } ++result; } } return result; } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryForTimeStep(TimeStepType timeStep) const { if ( IsValidTimeStep( timeStep ) ) { return m_GeometryVector[timeStep]; } else { return nullptr; } } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryForTimePoint( TimePointType timePoint ) const { if ( this->IsValidTimePoint( timePoint ) ) { const TimeStepType timeStep = this->TimePointToTimeStep( timePoint ); return this->GetGeometryForTimeStep( timeStep ); } else { return nullptr; } } mitk::BaseGeometry::Pointer mitk::ArbitraryTimeGeometry::GetGeometryCloneForTimeStep( TimeStepType timeStep ) const { if ( timeStep >= m_GeometryVector.size() ) return nullptr; return m_GeometryVector[timeStep]->Clone(); } bool mitk::ArbitraryTimeGeometry::IsValid() const { bool isValid = true; isValid &= m_GeometryVector.size() > 0; return isValid; } void mitk::ArbitraryTimeGeometry::ClearAllGeometries() { m_GeometryVector.clear(); m_MinimumTimePoints.clear(); m_MaximumTimePoints.clear(); } void mitk::ArbitraryTimeGeometry::ReserveSpaceForGeometries( TimeStepType numberOfGeometries ) { m_GeometryVector.reserve( numberOfGeometries ); m_MinimumTimePoints.reserve( numberOfGeometries ); m_MaximumTimePoints.reserve( numberOfGeometries ); } void mitk::ArbitraryTimeGeometry::Expand( mitk::TimeStepType size ) { m_GeometryVector.reserve( size ); const mitk::TimeStepType lastIndex = this->CountTimeSteps() - 1; const TimePointType minTP = this->GetMinimumTimePoint( lastIndex ); TimePointType maxTP = this->GetMaximumTimePoint( lastIndex ); const TimePointType duration = maxTP - minTP; while (m_GeometryVector.size() < size) { m_GeometryVector.push_back( Geometry3D::New().GetPointer() ); m_MinimumTimePoints.push_back( maxTP ); maxTP += duration; m_MaximumTimePoints.push_back( maxTP ); } } void mitk::ArbitraryTimeGeometry::ReplaceTimeStepGeometries(const BaseGeometry *geometry) { for ( auto pos = m_GeometryVector.begin(); pos != m_GeometryVector.end(); ++pos ) { *pos = geometry->Clone(); } } void mitk::ArbitraryTimeGeometry::SetTimeStepGeometry(BaseGeometry *geometry, TimeStepType timeStep) { assert( timeStep <= m_GeometryVector.size() ); if ( timeStep == m_GeometryVector.size() ) { m_GeometryVector.push_back( geometry ); } m_GeometryVector[timeStep] = geometry; } itk::LightObject::Pointer mitk::ArbitraryTimeGeometry::InternalClone() const { itk::LightObject::Pointer parent = Superclass::InternalClone(); ArbitraryTimeGeometry::Pointer newTimeGeometry = dynamic_cast(parent.GetPointer()); newTimeGeometry->m_MinimumTimePoints = this->m_MinimumTimePoints; newTimeGeometry->m_MaximumTimePoints = this->m_MaximumTimePoints; newTimeGeometry->m_GeometryVector.clear(); for (TimeStepType i = 0; i < CountTimeSteps(); ++i) { newTimeGeometry->m_GeometryVector.push_back( this->m_GeometryVector[i]->Clone() ); } return parent; } void mitk::ArbitraryTimeGeometry::AppendNewTimeStep(BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint) { if ( !geometry ) { mitkThrow() << "Cannot append geometry to time geometry. Invalid geometry passed (nullptr pointer)."; } if (maximumTimePoint < minimumTimePoint) { mitkThrow() << "Cannot append geometry to time geometry. Time bound conflict. Maxmimum time point ("< minimumTimePoint ) { mitkThrow() << "Cannot append geometry to time geometry. Time bound conflict new time point and currently last time point overlapp."; } } m_GeometryVector.push_back( geometry ); m_MinimumTimePoints.push_back( minimumTimePoint ); m_MaximumTimePoints.push_back( maximumTimePoint ); } void mitk::ArbitraryTimeGeometry::AppendNewTimeStepClone(const BaseGeometry *geometry, TimePointType minimumTimePoint, TimePointType maximumTimePoint) { BaseGeometry::Pointer clone = geometry->Clone(); this->AppendNewTimeStep(clone, minimumTimePoint, maximumTimePoint); }; void mitk::ArbitraryTimeGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf( os, indent ); os << indent << " MinimumTimePoint: " << this->GetMinimumTimePoint() << " ms" << std::endl; os << indent << " MaximumTimePoint: " << this->GetMaximumTimePoint() << " ms" << std::endl; os << std::endl; os << indent << " min TimeBounds: " << std::endl; for (TimeStepType i = 0; i < m_MinimumTimePoints.size(); ++i) { os << indent.GetNextIndent() << "Step " << i << ": " << m_MinimumTimePoints[i] << " ms" << std::endl; } os << std::endl; os << indent << " max TimeBounds: " << std::endl; for (TimeStepType i = 0; i < m_MaximumTimePoints.size(); ++i) { os << indent.GetNextIndent() << "Step " << i << ": " << m_MaximumTimePoints[i] << " ms" << std::endl; } } + +bool mitk::ArbitraryTimeGeometry::HasCollapsedFinalTimeStep() const +{ + bool result = false; + + if (!m_MaximumTimePoints.empty() && !m_MinimumTimePoints.empty()) + { + result = m_MinimumTimePoints.back() == m_MaximumTimePoints.back(); + } + + return result; +} diff --git a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp index d5bfa354e6..c2547de34b 100644 --- a/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp +++ b/Modules/Core/src/DataManagement/mitkBaseGeometry.cpp @@ -1,1003 +1,1105 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include +#include #include #include #include "mitkApplyTransformMatrixOperation.h" #include "mitkBaseGeometry.h" #include "mitkGeometryTransformHolder.h" #include "mitkInteractionConst.h" #include "mitkMatrixConvert.h" #include "mitkModifiedLock.h" #include "mitkPointOperation.h" #include "mitkRestorePlanePositionOperation.h" #include "mitkRotationOperation.h" #include "mitkScaleOperation.h" #include "mitkVector.h" +#include "mitkMatrix.h" mitk::BaseGeometry::BaseGeometry() : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(0), m_IndexToWorldTransformLastModified(0), m_ImageGeometry(false), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(); Initialize(); } mitk::BaseGeometry::BaseGeometry(const BaseGeometry &other) : Superclass(), mitk::OperationActor(), m_FrameOfReferenceID(other.m_FrameOfReferenceID), m_IndexToWorldTransformLastModified(other.m_IndexToWorldTransformLastModified), m_ImageGeometry(other.m_ImageGeometry), m_ModifiedLockFlag(false), m_ModifiedCalledFlag(false) { m_GeometryTransform = new GeometryTransformHolder(*other.GetGeometryTransformHolder()); other.InitializeGeometry(this); } mitk::BaseGeometry::~BaseGeometry() { delete m_GeometryTransform; } void mitk::BaseGeometry::SetVtkMatrixDeepCopy(vtkTransform *vtktransform) { m_GeometryTransform->SetVtkMatrixDeepCopy(vtktransform); } const mitk::Point3D mitk::BaseGeometry::GetOrigin() const { return m_GeometryTransform->GetOrigin(); } void mitk::BaseGeometry::SetOrigin(const Point3D &origin) { mitk::ModifiedLock lock(this); if (origin != GetOrigin()) { m_GeometryTransform->SetOrigin(origin); Modified(); } } const mitk::Vector3D mitk::BaseGeometry::GetSpacing() const { return m_GeometryTransform->GetSpacing(); } void mitk::BaseGeometry::Initialize() { float b[6] = {0, 1, 0, 1, 0, 1}; SetFloatBounds(b); m_GeometryTransform->Initialize(); m_FrameOfReferenceID = 0; m_ImageGeometry = false; } void mitk::BaseGeometry::SetFloatBounds(const float bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } void mitk::BaseGeometry::SetFloatBounds(const double bounds[6]) { mitk::BoundingBox::BoundsArrayType b; const double *input = bounds; int i = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); i < 6; ++i) *it++ = (mitk::ScalarType)*input++; SetBounds(b); } /** Initialize the geometry */ void mitk::BaseGeometry::InitializeGeometry(BaseGeometry *newGeometry) const { newGeometry->SetBounds(m_BoundingBox->GetBounds()); newGeometry->SetFrameOfReferenceID(GetFrameOfReferenceID()); newGeometry->InitializeGeometryTransformHolder(this); newGeometry->m_ImageGeometry = m_ImageGeometry; } void mitk::BaseGeometry::InitializeGeometryTransformHolder(const BaseGeometry *otherGeometry) { this->m_GeometryTransform->Initialize(otherGeometry->GetGeometryTransformHolder()); } /** Set the bounds */ void mitk::BaseGeometry::SetBounds(const BoundsArrayType &bounds) { mitk::ModifiedLock lock(this); this->CheckBounds(bounds); m_BoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < m_NDimensions; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } m_BoundingBox->SetPoints(pointscontainer); m_BoundingBox->ComputeBoundingBox(); this->Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransform(mitk::AffineTransform3D *transform) { mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransform(transform); Modified(); } void mitk::BaseGeometry::SetIndexToWorldTransformWithoutChangingSpacing(mitk::AffineTransform3D *transform) { // security check mitk::Vector3D originalSpacing = this->GetSpacing(); mitk::ModifiedLock lock(this); CheckIndexToWorldTransform(transform); m_GeometryTransform->SetIndexToWorldTransformWithoutChangingSpacing(transform); Modified(); // Security check. Spacig must not have changed if (!mitk::Equal(originalSpacing, this->GetSpacing())) { MITK_WARN << "Spacing has changed in a method, where the spacing must not change."; assert(false); } } const mitk::BaseGeometry::BoundsArrayType mitk::BaseGeometry::GetBounds() const { assert(m_BoundingBox.IsNotNull()); return m_BoundingBox->GetBounds(); } bool mitk::BaseGeometry::IsValid() const { return true; } void mitk::BaseGeometry::SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { PreSetSpacing(aSpacing); _SetSpacing(aSpacing, enforceSetSpacing); } void mitk::BaseGeometry::_SetSpacing(const mitk::Vector3D &aSpacing, bool enforceSetSpacing) { m_GeometryTransform->SetSpacing(aSpacing, enforceSetSpacing); } mitk::Vector3D mitk::BaseGeometry::GetAxisVector(unsigned int direction) const { Vector3D frontToBack; frontToBack.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction)); frontToBack *= GetExtent(direction); return frontToBack; } mitk::ScalarType mitk::BaseGeometry::GetExtent(unsigned int direction) const { assert(m_BoundingBox.IsNotNull()); if (direction >= m_NDimensions) mitkThrow() << "Direction is too big. This geometry is for 3D Data"; BoundsArrayType bounds = m_BoundingBox->GetBounds(); return bounds[direction * 2 + 1] - bounds[direction * 2]; } bool mitk::BaseGeometry::Is2DConvertable() { bool isConvertableWithoutLoss = true; do { if (this->GetSpacing()[2] != 1) { isConvertableWithoutLoss = false; break; } if (this->GetOrigin()[2] != 0) { isConvertableWithoutLoss = false; break; } mitk::Vector3D col0, col1, col2; col0.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0)); col1.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1)); col2.SetVnlVector(this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2)); if ((col0[2] != 0) || (col1[2] != 0) || (col2[0] != 0) || (col2[1] != 0) || (col2[2] != 1)) { isConvertableWithoutLoss = false; break; } } while (false); return isConvertableWithoutLoss; } mitk::Point3D mitk::BaseGeometry::GetCenter() const { assert(m_BoundingBox.IsNotNull()); Point3D c = m_BoundingBox->GetCenter(); if (m_ImageGeometry) { // Get Center returns the middel of min and max pixel index. In corner based images, this is the right position. // In center based images (imageGeometry == true), the index needs to be shifted back. c[0] -= 0.5; c[1] -= 0.5; c[2] -= 0.5; } this->IndexToWorld(c, c); return c; } double mitk::BaseGeometry::GetDiagonalLength2() const { Vector3D diagonalvector = GetCornerPoint() - GetCornerPoint(false, false, false); return diagonalvector.GetSquaredNorm(); } double mitk::BaseGeometry::GetDiagonalLength() const { return sqrt(GetDiagonalLength2()); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(int id) const { assert(id >= 0); assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; switch (id) { case 0: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[4]); break; case 1: FillVector3D(cornerpoint, bounds[0], bounds[2], bounds[5]); break; case 2: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[4]); break; case 3: FillVector3D(cornerpoint, bounds[0], bounds[3], bounds[5]); break; case 4: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[4]); break; case 5: FillVector3D(cornerpoint, bounds[1], bounds[2], bounds[5]); break; case 6: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[4]); break; case 7: FillVector3D(cornerpoint, bounds[1], bounds[3], bounds[5]); break; default: { itkExceptionMacro(<< "A cube only has 8 corners. These are labeled 0-7."); } } if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::Point3D mitk::BaseGeometry::GetCornerPoint(bool xFront, bool yFront, bool zFront) const { assert(this->IsBoundingBoxNull() == false); BoundingBox::BoundsArrayType bounds = this->GetBoundingBox()->GetBounds(); Point3D cornerpoint; cornerpoint[0] = (xFront ? bounds[0] : bounds[1]); cornerpoint[1] = (yFront ? bounds[2] : bounds[3]); cornerpoint[2] = (zFront ? bounds[4] : bounds[5]); if (m_ImageGeometry) { // Here i have to adjust the 0.5 offset manually, because the cornerpoint is the corner of the // bounding box. The bounding box itself is no image, so it is corner-based FillVector3D(cornerpoint, cornerpoint[0] - 0.5, cornerpoint[1] - 0.5, cornerpoint[2] - 0.5); } return this->GetIndexToWorldTransform()->TransformPoint(cornerpoint); } mitk::ScalarType mitk::BaseGeometry::GetExtentInMM(int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction).magnitude() * GetExtent(direction); } void mitk::BaseGeometry::SetExtentInMM(int direction, ScalarType extentInMM) { mitk::ModifiedLock lock(this); ScalarType len = GetExtentInMM(direction); if (fabs(len - extentInMM) >= mitk::eps) { AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = m_GeometryTransform->GetVnlMatrix(); if (len > extentInMM) vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) / len * extentInMM); else vnlmatrix.set_column(direction, vnlmatrix.get_column(direction) * extentInMM / len); Matrix3D matrix; matrix = vnlmatrix; m_GeometryTransform->SetMatrix(matrix); Modified(); } } bool mitk::BaseGeometry::IsInside(const mitk::Point3D &p) const { mitk::Point3D index; WorldToIndex(p, index); return IsIndexInside(index); } bool mitk::BaseGeometry::IsIndexInside(const mitk::Point3D &index) const { bool inside = false; // if it is an image geometry, we need to convert the index to discrete values // this is done by applying the rounding function also used in WorldToIndex (see line 323) if (m_ImageGeometry) { mitk::Point3D discretIndex; discretIndex[0] = itk::Math::RoundHalfIntegerUp(index[0]); discretIndex[1] = itk::Math::RoundHalfIntegerUp(index[1]); discretIndex[2] = itk::Math::RoundHalfIntegerUp(index[2]); inside = this->GetBoundingBox()->IsInside(discretIndex); // we have to check if the index is at the upper border of each dimension, // because the boundingbox is not centerbased if (inside) { const BoundingBox::BoundsArrayType &bounds = this->GetBoundingBox()->GetBounds(); if ((discretIndex[0] == bounds[1]) || (discretIndex[1] == bounds[3]) || (discretIndex[2] == bounds[5])) inside = false; } } else inside = this->GetBoundingBox()->IsInside(index); return inside; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D &pt_mm, mitk::Point3D &pt_units) const { mitk::Vector3D tempIn, tempOut; const TransformType::OffsetType &offset = this->GetIndexToWorldTransform()->GetOffset(); tempIn = pt_mm.GetVectorFromOrigin() - offset; WorldToIndex(tempIn, tempOut); pt_units = tempOut; } void mitk::BaseGeometry::WorldToIndex(const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { // Get WorldToIndex transform if (m_IndexToWorldTransformLastModified != this->GetIndexToWorldTransform()->GetMTime()) { if (!m_InvertedTransform) { m_InvertedTransform = TransformType::New(); } if (!this->GetIndexToWorldTransform()->GetInverse(m_InvertedTransform.GetPointer())) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed."); } m_IndexToWorldTransformLastModified = this->GetIndexToWorldTransform()->GetMTime(); } // Check for valid matrix inversion const TransformType::MatrixType &inverse = m_InvertedTransform->GetMatrix(); if (inverse.GetVnlMatrix().has_nans()) { itkExceptionMacro("Internal ITK matrix inversion error, cannot proceed. Matrix was: " << std::endl << this->GetIndexToWorldTransform()->GetMatrix() << "Suggested inverted matrix is:" << std::endl << inverse); } vec_units = inverse * vec_mm; } void mitk::BaseGeometry::WorldToIndex(const mitk::Point3D & /*atPt3d_mm*/, const mitk::Vector3D &vec_mm, mitk::Vector3D &vec_units) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::WorldToIndex(point, vec, vec). Use " "BaseGeometry::WorldToIndex(vec, vec) instead!"; this->WorldToIndex(vec_mm, vec_units); } mitk::VnlVector mitk::BaseGeometry::GetOriginVnl() const { return GetOrigin().GetVnlVector(); } vtkLinearTransform *mitk::BaseGeometry::GetVtkTransform() const { return m_GeometryTransform->GetVtkTransform(); } void mitk::BaseGeometry::SetIdentity() { mitk::ModifiedLock lock(this); m_GeometryTransform->SetIdentity(); Modified(); } void mitk::BaseGeometry::Compose(const mitk::BaseGeometry::TransformType *other, bool pre) { mitk::ModifiedLock lock(this); m_GeometryTransform->Compose(other, pre); Modified(); } void mitk::BaseGeometry::Compose(const vtkMatrix4x4 *vtkmatrix, bool pre) { mitk::BaseGeometry::TransformType::Pointer itkTransform = mitk::BaseGeometry::TransformType::New(); TransferVtkMatrixToItkTransform(vtkmatrix, itkTransform.GetPointer()); Compose(itkTransform, pre); } void mitk::BaseGeometry::Translate(const Vector3D &vector) { if ((vector[0] != 0) || (vector[1] != 0) || (vector[2] != 0)) { this->SetOrigin(this->GetOrigin() + vector); } } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D &pt_units, mitk::Point3D &pt_mm) const { pt_mm = this->GetIndexToWorldTransform()->TransformPoint(pt_units); } void mitk::BaseGeometry::IndexToWorld(const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { vec_mm = this->GetIndexToWorldTransform()->TransformVector(vec_units); } void mitk::BaseGeometry::ExecuteOperation(Operation *operation) { mitk::ModifiedLock lock(this); vtkTransform *vtktransform = vtkTransform::New(); vtktransform->SetMatrix(this->GetVtkMatrix()); switch (operation->GetOperationType()) { case OpNOTHING: break; case OpMOVE: { auto *pointOp = dynamic_cast(operation); if (pointOp == nullptr) { MITK_ERROR << "Point move operation is null!"; return; } mitk::Point3D newPos = pointOp->GetPoint(); ScalarType data[3]; vtktransform->GetPosition(data); vtktransform->PostMultiply(); vtktransform->Translate(newPos[0], newPos[1], newPos[2]); vtktransform->PreMultiply(); break; } case OpSCALE: { auto *scaleOp = dynamic_cast(operation); if (scaleOp == nullptr) { MITK_ERROR << "Scale operation is null!"; return; } mitk::Point3D newScale = scaleOp->GetScaleFactor(); ScalarType scalefactor[3]; scalefactor[0] = 1 + (newScale[0] / GetMatrixColumn(0).magnitude()); scalefactor[1] = 1 + (newScale[1] / GetMatrixColumn(1).magnitude()); scalefactor[2] = 1 + (newScale[2] / GetMatrixColumn(2).magnitude()); mitk::Point3D anchor = scaleOp->GetScaleAnchorPoint(); vtktransform->PostMultiply(); vtktransform->Translate(-anchor[0], -anchor[1], -anchor[2]); vtktransform->Scale(scalefactor[0], scalefactor[1], scalefactor[2]); vtktransform->Translate(anchor[0], anchor[1], anchor[2]); break; } case OpROTATE: { auto *rotateOp = dynamic_cast(operation); if (rotateOp == nullptr) { MITK_ERROR << "Rotation operation is null!"; return; } Vector3D rotationVector = rotateOp->GetVectorOfRotation(); Point3D center = rotateOp->GetCenterOfRotation(); ScalarType angle = rotateOp->GetAngleOfRotation(); vtktransform->PostMultiply(); vtktransform->Translate(-center[0], -center[1], -center[2]); vtktransform->RotateWXYZ(angle, rotationVector[0], rotationVector[1], rotationVector[2]); vtktransform->Translate(center[0], center[1], center[2]); vtktransform->PreMultiply(); break; } case OpRESTOREPLANEPOSITION: { // Copy necessary to avoid vtk warning vtkMatrix4x4 *matrix = vtkMatrix4x4::New(); TransferItkTransformToVtkMatrix( dynamic_cast(operation)->GetTransform().GetPointer(), matrix); vtktransform->SetMatrix(matrix); matrix->Delete(); break; } case OpAPPLYTRANSFORMMATRIX: { auto *applyMatrixOp = dynamic_cast(operation); vtktransform->SetMatrix(applyMatrixOp->GetMatrix()); break; } default: vtktransform->Delete(); return; } this->SetVtkMatrixDeepCopy(vtktransform); Modified(); vtktransform->Delete(); } mitk::VnlVector mitk::BaseGeometry::GetMatrixColumn(unsigned int direction) const { return this->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(direction); } mitk::BoundingBox::Pointer mitk::BaseGeometry::CalculateBoundingBoxRelativeToTransform( const mitk::AffineTransform3D *transform) const { mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; if (transform != nullptr) { mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); transform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(GetCornerPoint(i))); } else { for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, GetCornerPoint(i)); } mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); return result; } const std::string mitk::BaseGeometry::GetTransformAsString(TransformType *transformType) { std::ostringstream out; out << '['; for (int i = 0; i < 3; ++i) { out << '['; for (int j = 0; j < 3; ++j) out << transformType->GetMatrix().GetVnlMatrix().get(i, j) << ' '; out << ']'; } out << "]["; for (int i = 0; i < 3; ++i) out << transformType->GetOffset()[i] << ' '; out << "]\0"; return out.str(); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrix(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); } void mitk::BaseGeometry::SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkMatrix4x4 *vtkmatrix) { m_GeometryTransform->SetIndexToWorldTransformByVtkMatrixWithoutChangingSpacing(vtkmatrix); } void mitk::BaseGeometry::IndexToWorld(const mitk::Point3D & /*atPt3d_units*/, const mitk::Vector3D &vec_units, mitk::Vector3D &vec_mm) const { MITK_WARN << "Warning! Call of the deprecated function BaseGeometry::IndexToWorld(point, vec, vec). Use " "BaseGeometry::IndexToWorld(vec, vec) instead!"; // vec_mm = m_IndexToWorldTransform->TransformVector(vec_units); this->IndexToWorld(vec_units, vec_mm); } vtkMatrix4x4 *mitk::BaseGeometry::GetVtkMatrix() { return m_GeometryTransform->GetVtkMatrix(); } bool mitk::BaseGeometry::IsBoundingBoxNull() const { return m_BoundingBox.IsNull(); } bool mitk::BaseGeometry::IsIndexToWorldTransformNull() const { return m_GeometryTransform->IsIndexToWorldTransformNull(); } void mitk::BaseGeometry::ChangeImageGeometryConsideringOriginOffset(const bool isAnImageGeometry) { // If Geometry is switched to ImageGeometry, you have to put an offset to the origin, because // imageGeometries origins are pixel-center-based // ... and remove the offset, if you switch an imageGeometry back to a normal geometry // For more information please see the Geometry documentation page if (m_ImageGeometry == isAnImageGeometry) return; const BoundingBox::BoundsArrayType &boundsarray = this->GetBoundingBox()->GetBounds(); Point3D originIndex; FillVector3D(originIndex, boundsarray[0], boundsarray[2], boundsarray[4]); if (isAnImageGeometry == true) FillVector3D(originIndex, originIndex[0] + 0.5, originIndex[1] + 0.5, originIndex[2] + 0.5); else FillVector3D(originIndex, originIndex[0] - 0.5, originIndex[1] - 0.5, originIndex[2] - 0.5); Point3D originWorld; originWorld = GetIndexToWorldTransform()->TransformPoint(originIndex); // instead could as well call IndexToWorld(originIndex,originWorld); SetOrigin(originWorld); this->SetImageGeometry(isAnImageGeometry); } void mitk::BaseGeometry::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " IndexToWorldTransform: "; if (this->IsIndexToWorldTransformNull()) os << "nullptr" << std::endl; else { // from itk::MatrixOffsetTransformBase unsigned int i, j; os << std::endl; os << indent << "Matrix: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetMatrix()[i][j] << " "; } os << std::endl; } os << indent << "Offset: " << this->GetIndexToWorldTransform()->GetOffset() << std::endl; os << indent << "Center: " << this->GetIndexToWorldTransform()->GetCenter() << std::endl; os << indent << "Translation: " << this->GetIndexToWorldTransform()->GetTranslation() << std::endl; os << indent << "Inverse: " << std::endl; for (i = 0; i < 3; i++) { os << indent.GetNextIndent(); for (j = 0; j < 3; j++) { os << this->GetIndexToWorldTransform()->GetInverseMatrix()[i][j] << " "; } os << std::endl; } // from itk::ScalableAffineTransform os << indent << "Scale : "; for (i = 0; i < 3; i++) { os << this->GetIndexToWorldTransform()->GetScale()[i] << " "; } os << std::endl; } os << indent << " BoundingBox: "; if (this->IsBoundingBoxNull()) os << "nullptr" << std::endl; else { os << indent << "( "; for (unsigned int i = 0; i < 3; i++) { os << this->GetBoundingBox()->GetBounds()[2 * i] << "," << this->GetBoundingBox()->GetBounds()[2 * i + 1] << " "; } os << " )" << std::endl; } os << indent << " Origin: " << this->GetOrigin() << std::endl; os << indent << " ImageGeometry: " << this->GetImageGeometry() << std::endl; os << indent << " Spacing: " << this->GetSpacing() << std::endl; } void mitk::BaseGeometry::Modified() const { if (!m_ModifiedLockFlag) Superclass::Modified(); else m_ModifiedCalledFlag = true; } mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::AffineTransform3D *mitk::BaseGeometry::GetIndexToWorldTransform() const { return m_GeometryTransform->GetIndexToWorldTransform(); } const mitk::GeometryTransformHolder *mitk::BaseGeometry::GetGeometryTransformHolder() const { return m_GeometryTransform; } bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType *leftHandSide, const mitk::BaseGeometry::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal( const mitk::Geometry3D::BoundingBoxType *leftHandSide, const " "mitk::Geometry3D::BoundingBoxType *rightHandSide, ScalarType eps, bool verbose ) does not with nullptr " "pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry::BoundingBoxType &leftHandSide, const mitk::BaseGeometry::BoundingBoxType &rightHandSide, ScalarType eps, bool verbose) { bool result = true; BaseGeometry::BoundsArrayType rightBounds = rightHandSide.GetBounds(); BaseGeometry::BoundsArrayType leftBounds = leftHandSide.GetBounds(); BaseGeometry::BoundsArrayType::Iterator itLeft = leftBounds.Begin(); for (BaseGeometry::BoundsArrayType::Iterator itRight = rightBounds.Begin(); itRight != rightBounds.End(); ++itRight) { if ((!mitk::Equal(*itLeft, *itRight, eps))) { if (verbose) { MITK_INFO << "[( Geometry3D::BoundingBoxType )] bounds are not equal."; MITK_INFO << "rightHandSide is " << setprecision(12) << *itRight << " : leftHandSide is " << *itLeft << " and tolerance is " << eps; } result = false; } itLeft++; } return result; } bool mitk::Equal(const mitk::BaseGeometry *leftHandSide, const mitk::BaseGeometry *rightHandSide, ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal(const mitk::Geometry3D *leftHandSide, const mitk::Geometry3D *rightHandSide, ScalarType " "eps, bool verbose) does not with nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry &leftHandSide, const mitk::BaseGeometry &rightHandSide, ScalarType eps, bool verbose) { bool result = true; // Compare spacings if (!mitk::Equal(leftHandSide.GetSpacing(), rightHandSide.GetSpacing(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Spacing differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetSpacing() << " : leftHandSide is " << leftHandSide.GetSpacing() << " and tolerance is " << eps; } result = false; } // Compare Origins if (!mitk::Equal(leftHandSide.GetOrigin(), rightHandSide.GetOrigin(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Origin differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetOrigin() << " : leftHandSide is " << leftHandSide.GetOrigin() << " and tolerance is " << eps; } result = false; } // Compare Axis and Extents for (unsigned int i = 0; i < 3; ++i) { if (!mitk::Equal(leftHandSide.GetAxisVector(i), rightHandSide.GetAxisVector(i), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] AxisVector #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetAxisVector(i) << " : leftHandSide is " << leftHandSide.GetAxisVector(i) << " and tolerance is " << eps; } result = false; } if (!mitk::Equal(leftHandSide.GetExtent(i), rightHandSide.GetExtent(i), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D )] Extent #" << i << " differ"; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetExtent(i) << " : leftHandSide is " << leftHandSide.GetExtent(i) << " and tolerance is " << eps; } result = false; } } // Compare ImageGeometry Flag if (rightHandSide.GetImageGeometry() != leftHandSide.GetImageGeometry()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetImageGeometry() << " : leftHandSide is " << leftHandSide.GetImageGeometry(); } result = false; } // Compare FrameOfReference ID if (rightHandSide.GetFrameOfReferenceID() != leftHandSide.GetFrameOfReferenceID()) { if (verbose) { MITK_INFO << "[( Geometry3D )] GetFrameOfReferenceID is different."; MITK_INFO << "rightHandSide is " << rightHandSide.GetFrameOfReferenceID() << " : leftHandSide is " << leftHandSide.GetFrameOfReferenceID(); } result = false; } // Compare BoundingBoxes if (!mitk::Equal(*leftHandSide.GetBoundingBox(), *rightHandSide.GetBoundingBox(), eps, verbose)) { result = false; } // Compare IndexToWorldTransform Matrix if (!mitk::Equal(*leftHandSide.GetIndexToWorldTransform(), *rightHandSide.GetIndexToWorldTransform(), eps, verbose)) { result = false; } return result; } bool mitk::Equal(const mitk::BaseGeometry::TransformType *leftHandSide, const mitk::BaseGeometry::TransformType *rightHandSide, ScalarType eps, bool verbose) { if ((leftHandSide == nullptr) || (rightHandSide == nullptr)) { MITK_ERROR << "mitk::Equal(const Geometry3D::TransformType *leftHandSide, const Geometry3D::TransformType " "*rightHandSide, ScalarType eps, bool verbose ) does not with nullptr pointer input."; return false; } return Equal(*leftHandSide, *rightHandSide, eps, verbose); } bool mitk::Equal(const mitk::BaseGeometry::TransformType &leftHandSide, const mitk::BaseGeometry::TransformType &rightHandSide, ScalarType eps, bool verbose) { // Compare IndexToWorldTransform Matrix if (!mitk::MatrixEqualElementWise(leftHandSide.GetMatrix(), rightHandSide.GetMatrix(), eps)) { if (verbose) { MITK_INFO << "[( Geometry3D::TransformType )] Index to World Transformation matrix differs."; MITK_INFO << "rightHandSide is " << setprecision(12) << rightHandSide.GetMatrix() << " : leftHandSide is " << leftHandSide.GetMatrix() << " and tolerance is " << eps; } return false; } return true; } + +bool mitk::IsSubGeometry(const mitk::BaseGeometry& testGeo, + const mitk::BaseGeometry& referenceGeo, + ScalarType eps, + bool verbose) +{ + bool result = true; + + // Compare spacings (must be equal) + const auto testedSpacing = testGeo.GetSpacing(); + if (!mitk::Equal(testedSpacing, referenceGeo.GetSpacing(), eps)) + { + if (verbose) + { + MITK_INFO << "[( Geometry3D )] Spacing differs."; + MITK_INFO << "testedGeometry is " << setprecision(12) << testedSpacing << " : referenceGeometry is " + << referenceGeo.GetSpacing() << " and tolerance is " << eps; + } + result = false; + } + + // Compare ImageGeometry Flag (must be equal) + if (referenceGeo.GetImageGeometry() != testGeo.GetImageGeometry()) + { + if (verbose) + { + MITK_INFO << "[( Geometry3D )] GetImageGeometry is different."; + MITK_INFO << "referenceGeo is " << referenceGeo.GetImageGeometry() << " : testGeo is " + << testGeo.GetImageGeometry(); + } + result = false; + } + + // Compare IndexToWorldTransform Matrix (must be equal -> same axis directions) + if (!Equal(*(testGeo.GetIndexToWorldTransform()), *(referenceGeo.GetIndexToWorldTransform()), eps, verbose)) + { + result = false; + } + + // Compare BoundingBoxes (must be <=, thus corners of the tests geom must be inside the reference) + for (int i = 0; i<8; ++i) + { + auto testCorner = testGeo.GetCornerPoint(i); + if (testGeo.GetImageGeometry()) + { + //if it is an image geometry the upper bounds indicate the first index that is not inside + //the geometry. Thus we will get the lower corener of the "first" voxel outside, + //but we need the corner of the "last" voxel inside the test geometry. + //To achive that we supstract one spacing from each index elment that is constructed + //by an upper bound value (see implementation of BaseGeometry::GetCorner(). + std::bitset bs(i); + if (bs.test(0)) + { + testCorner[2] -= testedSpacing[2]; + } + if (bs.test(1)) + { + testCorner[1] -= testedSpacing[1]; + } + if (bs.test(2)) + { + testCorner[0] -= testedSpacing[0]; + } + } + + if (!referenceGeo.IsInside(testCorner)) + { + if (verbose) + { + MITK_INFO << "[( Geometry3D )] corner point is not inside. "; + MITK_INFO << "referenceGeo is " << setprecision(12) << referenceGeo << " : tested corner is " + << testGeo.GetCornerPoint(i); + } + result = false; + } + } + + // check grid of test geometry is on the grid of the reference geometry. This is important as the + // boundingbox is only checked for containing the tested geometry, but if a corner (one is enough + // as we know that axis and spacing are equal) of the tested geometry is on the grid it is really + // a sub geometry (as they have the same spacing and axis). + auto cornerOffset = testGeo.GetCornerPoint(0) - referenceGeo.GetCornerPoint(0); + for (unsigned int i = 0; i < 3; ++i) + { + auto pixelCountContinous = cornerOffset[i] / testedSpacing[i]; + auto pixelCount = std::round(pixelCountContinous); + if (std::abs(pixelCount - pixelCountContinous) > eps) + { + if (verbose) + { + MITK_INFO << "[( Geometry3D )] Tested geometry is not on the grid of the reference geometry. "; + MITK_INFO << "referenceGeo is " << setprecision(15) << referenceGeo << " : tested corner offset in pixels is " + << pixelCountContinous << " for axis "<GetData(); + if (data) + { + if (nullptr != data && m_RefGeometry.IsNotNull()) + { //check only one time point. + mitk::BaseGeometry::Pointer testGeometry = data->GetGeometry(); + if (this->m_UseTimePoint) + { + testGeometry = data->GetTimeGeometry()->GetGeometryForTimePoint(m_TimePoint); + } + + if (testGeometry.IsNotNull()) + { + return IsSubGeometry(*testGeometry, *m_RefGeometry, this->m_CheckPrecision, false); + } + } + } + + return false; +} diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index d4666d22ed..55a4b86dc9 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,708 +1,724 @@ /*============================================================================ 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 "mitkItkImageIO.h" #include #include #include #include #include #include #include #include +#include #include #include #include #include #include #include namespace mitk { const char *const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char *const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char *const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; + const char* const PROPERTY_KEY_UID = "org_mitk_uid"; ItkImageIO::ItkImageIO(const ItkImageIO &other) : AbstractFileIO(other), m_ImageIO(dynamic_cast(other.m_ImageIO->Clone().GetPointer())) { this->InitializeDefaultMetaDataKeys(); } std::vector ItkImageIO::FixUpImageIOExtensions(const std::string &imageIOName) { std::vector extensions; // Try to fix-up some known ITK image IO classes if (imageIOName == "GiplImageIO") { extensions.push_back("gipl"); extensions.push_back("gipl.gz"); } else if (imageIOName == "GDCMImageIO") { extensions.push_back("gdcm"); extensions.push_back("dcm"); extensions.push_back("DCM"); extensions.push_back("dc3"); extensions.push_back("DC3"); extensions.push_back("ima"); extensions.push_back("img"); } else if (imageIOName == "PNGImageIO") { extensions.push_back("png"); extensions.push_back("PNG"); } else if (imageIOName == "StimulateImageIO") { extensions.push_back("spr"); } else if (imageIOName == "HDF5ImageIO") { extensions.push_back("hdf"); extensions.push_back("h4"); extensions.push_back("hdf4"); extensions.push_back("h5"); extensions.push_back("hdf5"); extensions.push_back("he4"); extensions.push_back("he5"); extensions.push_back("hd5"); } else if ("GE4ImageIO" == imageIOName || "GE5ImageIO" == imageIOName || "Bruker2dseqImageIO" == imageIOName) { extensions.push_back(""); } if (!extensions.empty()) { MITK_DEBUG << "Fixing up known extensions for " << imageIOName; } return extensions; } void ItkImageIO::FixUpCustomMimeTypeName(const std::string &imageIOName, CustomMimeType &customMimeType) { if ("GE4ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge4"); } else if ("GE5ImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "ge5"); } else if ("Bruker2dseqImageIO" == imageIOName) { customMimeType.SetName(this->AbstractFileReader::GetMimeTypePrefix() + "bruker2dseq"); } } ItkImageIO::ItkImageIO(itk::ImageIOBase::Pointer imageIO) : AbstractFileIO(Image::GetStaticNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); std::vector readExtensions = m_ImageIO->GetSupportedReadExtensions(); if (readExtensions.empty()) { std::string imageIOName = m_ImageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide read extensions"; readExtensions = FixUpImageIOExtensions(imageIOName); } CustomMimeType customReaderMimeType; customReaderMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = readExtensions.begin(), endIter = readExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customReaderMimeType.AddExtension(extension); } auto extensions = customReaderMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customReaderMimeType); } this->AbstractFileReader::SetMimeType(customReaderMimeType); std::vector writeExtensions = imageIO->GetSupportedWriteExtensions(); if (writeExtensions.empty()) { std::string imageIOName = imageIO->GetNameOfClass(); MITK_DEBUG << "ITK ImageIOBase " << imageIOName << " does not provide write extensions"; writeExtensions = FixUpImageIOExtensions(imageIOName); } if (writeExtensions != readExtensions) { CustomMimeType customWriterMimeType; customWriterMimeType.SetCategory("Images"); for (std::vector::const_iterator iter = writeExtensions.begin(), endIter = writeExtensions.end(); iter != endIter; ++iter) { std::string extension = *iter; if (!extension.empty() && extension[0] == '.') { extension.assign(iter->begin() + 1, iter->end()); } customWriterMimeType.AddExtension(extension); } auto extensions = customWriterMimeType.GetExtensions(); if (extensions.empty() || (extensions.size() == 1 && extensions[0].empty())) { std::string imageIOName = m_ImageIO->GetNameOfClass(); FixUpCustomMimeTypeName(imageIOName, customWriterMimeType); } this->AbstractFileWriter::SetMimeType(customWriterMimeType); } std::string description = std::string("ITK ") + imageIO->GetNameOfClass(); this->SetReaderDescription(description); this->SetWriterDescription(description); this->RegisterService(); } ItkImageIO::ItkImageIO(const CustomMimeType &mimeType, itk::ImageIOBase::Pointer imageIO, int rank) : AbstractFileIO(Image::GetStaticNameOfClass(), mimeType, std::string("ITK ") + imageIO->GetNameOfClass()), m_ImageIO(imageIO) { if (m_ImageIO.IsNull()) { mitkThrow() << "ITK ImageIOBase argument must not be nullptr"; } this->AbstractFileReader::SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".image."); this->InitializeDefaultMetaDataKeys(); if (rank) { this->AbstractFileReader::SetRanking(rank); this->AbstractFileWriter::SetRanking(rank); } this->RegisterService(); } std::vector ConvertMetaDataObjectToTimePointList(const itk::MetaDataObjectBase* data) { const auto* timeGeometryTimeData = dynamic_cast*>(data); std::vector result; if (timeGeometryTimeData) { std::string dataStr = timeGeometryTimeData->GetMetaDataObjectValue(); std::stringstream stream(dataStr); TimePointType tp; while (stream >> tp) { result.push_back(tp); } } return result; }; itk::MetaDataObjectBase::Pointer ConvertTimePointListToMetaDataObject(const mitk::TimeGeometry* timeGeometry) { std::stringstream stream; stream << timeGeometry->GetTimeBounds(0)[0]; const auto maxTimePoints = timeGeometry->CountTimeSteps(); for (TimeStepType pos = 0; pos < maxTimePoints; ++pos) { stream << " " << timeGeometry->GetTimeBounds(pos)[1]; } auto result = itk::MetaDataObject::New(); result->SetMetaDataObjectValue(stream.str()); return result.GetPointer(); }; std::vector ItkImageIO::DoRead() { std::vector result; mitk::LocaleSwitch localeSwitch("C"); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. m_ImageIO->SetFileName(path); m_ImageIO->ReadImageInformation(); unsigned int ndim = m_ImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = m_ImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = m_ImageIO->GetDimensions(i); spacing[i] = m_ImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = m_ImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; m_ImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[m_ImageIO->GetImageSizeInBytes()]; m_ImageIO->Read(buffer); image->Initialize(MakePixelType(m_ImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); const itk::MetaDataDictionary &dictionary = m_ImageIO->GetMetaDataDictionary(); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = m_ImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData = nullptr; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast *>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string &key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer &x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast *>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); image->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey(false); for (const auto &defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } + // Handle UID + if (dictionary.HasKey(PROPERTY_KEY_UID)) + { + itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); + if (uidData.IsNotNull()) + { + mitk::UIDManipulator uidManipulator(image); + uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); + } + } + MITK_INFO << "...finished!"; result.push_back(image.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel ItkImageIO::GetReaderConfidenceLevel() const { return m_ImageIO->CanReadFile(GetLocalFileName().c_str()) ? IFileReader::Supported : IFileReader::Unsupported; } void ItkImageIO::Write() { const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { mitkThrow() << "Cannot write non-image data"; } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = image->GetGeometry()->Clone(); // Check if geometry information will be lost if (image->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = image->GetDimension(); const unsigned int *const dimensions = image->GetDimensions(); const mitk::PixelType pixelType = image->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO m_ImageIO->SetNumberOfDimensions(dimension); m_ImageIO->SetPixelType(pixelType.GetPixelType()); m_ImageIO->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); m_ImageIO->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { m_ImageIO->SetDimensions(i, dimensions[i]); m_ImageIO->SetSpacing(i, spacing4D[i]); m_ImageIO->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } m_ImageIO->SetDirection(i, axisDirection); ioRegion.SetSize(i, image->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, image->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available m_ImageIO->UseCompressionOn(); m_ImageIO->SetIORegion(ioRegion); m_ImageIO->SetFileName(path); // Handle time geometry const auto *arbitraryTG = dynamic_cast(image->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); m_ImageIO->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = image->GetPropertyList(); for (const auto &property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), key, value); } + // Handle UID + itk::EncapsulateMetaData(m_ImageIO->GetMetaDataDictionary(), PROPERTY_KEY_UID, image->GetUID()); + ImageReadAccessor imageAccess(image); LocaleSwitch localeSwitch2("C"); m_ImageIO->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } } AbstractFileIO::ConfidenceLevel ItkImageIO::GetWriterConfidenceLevel() const { // Check if the image dimension is supported const auto *image = dynamic_cast(this->GetInput()); if (image == nullptr) { // We cannot write a null object, DUH! return IFileWriter::Unsupported; } if (!m_ImageIO->SupportsDimension(image->GetDimension())) { // okay, dimension is not supported. We have to look at a special case: // 3D-Image with one slice. We can treat that as a 2D image. if ((image->GetDimension() == 3) && (image->GetSlicedGeometry()->GetSlices() == 1)) return IFileWriter::Supported; else return IFileWriter::Unsupported; } // Check if geometry information will be lost if (image->GetDimension() == 2 && !image->GetGeometry()->Is2DConvertable()) { return IFileWriter::PartiallySupported; } return IFileWriter::Supported; } ItkImageIO *ItkImageIO::IOClone() const { return new ItkImageIO(*this); } void ItkImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); } } diff --git a/Modules/Core/src/Rendering/mitkAnnotation.cpp b/Modules/Core/src/Rendering/mitkAnnotation.cpp index 3da8a601d1..8c7e1e092c 100644 --- a/Modules/Core/src/Rendering/mitkAnnotation.cpp +++ b/Modules/Core/src/Rendering/mitkAnnotation.cpp @@ -1,348 +1,348 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkAnnotation.h" #include "usGetModuleContext.h" const std::string mitk::Annotation::US_INTERFACE_NAME = "org.mitk.services.Annotation"; const std::string mitk::Annotation::US_PROPKEY_AnnotationNAME = US_INTERFACE_NAME + ".name"; const std::string mitk::Annotation::US_PROPKEY_ID = US_INTERFACE_NAME + ".id"; const std::string mitk::Annotation::US_PROPKEY_MODIFIED = US_INTERFACE_NAME + ".modified"; const std::string mitk::Annotation::US_PROPKEY_RENDERER_ID = US_INTERFACE_NAME + ".rendererId"; const std::string mitk::Annotation::US_PROPKEY_AR_ID = US_INTERFACE_NAME + ".arId"; mitk::Annotation::Annotation() : m_PropertyListModifiedObserverTag(0) { m_PropertyList = mitk::PropertyList::New(); itk::MemberCommand::Pointer _PropertyListModifiedCommand = itk::MemberCommand::New(); _PropertyListModifiedCommand->SetCallbackFunction(this, &mitk::Annotation::PropertyListModified); m_PropertyListModifiedObserverTag = m_PropertyList->AddObserver(itk::ModifiedEvent(), _PropertyListModifiedCommand); this->SetName(this->GetNameOfClass()); this->SetVisibility(true); this->SetOpacity(1.0); } void mitk::Annotation::PropertyListModified(const itk::Object * /*caller*/, const itk::EventObject &) { AnnotationModified(); } mitk::Annotation::~Annotation() { this->UnRegisterMicroservice(); } void mitk::Annotation::SetUSProperty(const std::string &propertyKey, us::Any value) { if (this->m_ServiceRegistration) { us::ServiceProperties props; std::vector propertyKeys; m_ServiceRegistration.GetReference().GetPropertyKeys(propertyKeys); for (std::string key : propertyKeys) { props[key] = m_ServiceRegistration.GetReference().GetProperty(key); } props[propertyKey] = value; m_ServiceRegistration.SetProperties(props); } } void mitk::Annotation::SetProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue) { this->m_PropertyList->SetProperty(propertyKey, propertyValue); } void mitk::Annotation::ReplaceProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue) { this->m_PropertyList->ReplaceProperty(propertyKey, propertyValue); } void mitk::Annotation::AddProperty(const std::string &propertyKey, const BaseProperty::Pointer &propertyValue, bool overwrite) { if ((overwrite) || (GetProperty(propertyKey) == nullptr)) { SetProperty(propertyKey, propertyValue); } } void mitk::Annotation::ConcatenatePropertyList(PropertyList *pList, bool replace) { m_PropertyList->ConcatenatePropertyList(pList, replace); } mitk::BaseProperty *mitk::Annotation::GetProperty(const std::string &propertyKey) const { mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty(propertyKey); if (property.IsNotNull()) return property; // only to satisfy compiler! return nullptr; } bool mitk::Annotation::GetBoolProperty(const std::string &propertyKey, bool &boolValue) const { mitk::BoolProperty::Pointer boolprop = dynamic_cast(GetProperty(propertyKey)); if (boolprop.IsNull()) return false; boolValue = boolprop->GetValue(); return true; } bool mitk::Annotation::GetIntProperty(const std::string &propertyKey, int &intValue) const { mitk::IntProperty::Pointer intprop = dynamic_cast(GetProperty(propertyKey)); if (intprop.IsNull()) return false; intValue = intprop->GetValue(); return true; } bool mitk::Annotation::GetFloatProperty(const std::string &propertyKey, float &floatValue) const { mitk::FloatProperty::Pointer floatprop = dynamic_cast(GetProperty(propertyKey)); if (floatprop.IsNull()) return false; floatValue = floatprop->GetValue(); return true; } bool mitk::Annotation::GetStringProperty(const std::string &propertyKey, std::string &string) const { mitk::StringProperty::Pointer stringProp = dynamic_cast(GetProperty(propertyKey)); if (stringProp.IsNull()) { return false; } else { // memcpy((void*)string, stringProp->GetValue(), strlen(stringProp->GetValue()) + 1 ); // looks dangerous string = stringProp->GetValue(); return true; } } void mitk::Annotation::SetIntProperty(const std::string &propertyKey, int intValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::IntProperty::New(intValue)); Modified(); } void mitk::Annotation::SetBoolProperty(const std::string &propertyKey, bool boolValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::BoolProperty::New(boolValue)); Modified(); } void mitk::Annotation::SetFloatProperty(const std::string &propertyKey, float floatValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::FloatProperty::New(floatValue)); Modified(); } void mitk::Annotation::SetDoubleProperty(const std::string &propertyKey, double doubleValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::DoubleProperty::New(doubleValue)); Modified(); } void mitk::Annotation::SetStringProperty(const std::string &propertyKey, const std::string &stringValue) { this->m_PropertyList->SetProperty(propertyKey, mitk::StringProperty::New(stringValue)); Modified(); } std::string mitk::Annotation::GetName() const { mitk::StringProperty *sp = dynamic_cast(this->GetProperty("name")); if (sp == nullptr) return ""; return sp->GetValue(); } void mitk::Annotation::SetName(const std::string &name) { this->SetStringProperty("name", name); } bool mitk::Annotation::GetName(std::string &nodeName, const std::string &propertyKey) const { return GetStringProperty(propertyKey, nodeName); } void mitk::Annotation::SetText(std::string text) { SetStringProperty("Text", text.c_str()); } std::string mitk::Annotation::GetText() const { std::string text; GetStringProperty("Text", text); return text; } void mitk::Annotation::SetFontSize(int fontSize) { SetIntProperty("FontSize", fontSize); } int mitk::Annotation::GetFontSize() const { int fontSize = 1; GetIntProperty("FontSize", fontSize); return fontSize; } bool mitk::Annotation::GetVisibility(bool &visible, const std::string &propertyKey) const { return GetBoolProperty(propertyKey, visible); } bool mitk::Annotation::IsVisible(const std::string &propertyKey, bool defaultIsOn) const { return IsOn(propertyKey, defaultIsOn); } bool mitk::Annotation::GetColor(float rgb[], const std::string &propertyKey) const { mitk::ColorProperty::Pointer colorprop = dynamic_cast(GetProperty(propertyKey)); if (colorprop.IsNull()) return false; memcpy(rgb, colorprop->GetColor().GetDataPointer(), 3 * sizeof(float)); return true; } void mitk::Annotation::SetColor(const mitk::Color &color, const std::string &propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(color); this->m_PropertyList->SetProperty(propertyKey, prop); } void mitk::Annotation::SetColor(float red, float green, float blue, const std::string &propertyKey) { float color[3]; color[0] = red; color[1] = green; color[2] = blue; SetColor(color, propertyKey); } void mitk::Annotation::SetColor(const float rgb[], const std::string &propertyKey) { mitk::ColorProperty::Pointer prop; prop = mitk::ColorProperty::New(rgb); this->m_PropertyList->SetProperty(propertyKey, prop); } bool mitk::Annotation::GetOpacity(float &opacity, const std::string &propertyKey) const { mitk::FloatProperty::Pointer opacityprop = dynamic_cast(GetProperty(propertyKey)); if (opacityprop.IsNull()) return false; opacity = opacityprop->GetValue(); return true; } void mitk::Annotation::SetOpacity(float opacity, const std::string &propertyKey) { mitk::FloatProperty::Pointer prop; prop = mitk::FloatProperty::New(opacity); this->m_PropertyList->SetProperty(propertyKey, prop); } void mitk::Annotation::SetVisibility(bool visible, const std::string &propertyKey) { mitk::BoolProperty::Pointer prop; prop = mitk::BoolProperty::New(visible); this->m_PropertyList->SetProperty(propertyKey, prop); Modified(); } bool mitk::Annotation::BaseLocalStorage::IsGenerateDataRequired(mitk::BaseRenderer *renderer, mitk::Annotation *Annotation) { if (m_LastGenerateDataTime < Annotation->GetMTime()) return true; if (m_LastGenerateDataTime < Annotation->GetPropertyList()->GetMTime()) return true; if (renderer && m_LastGenerateDataTime < renderer->GetTimeStepUpdateTime()) return true; return false; } mitk::Annotation::Bounds mitk::Annotation::GetBoundsOnDisplay(mitk::BaseRenderer *) const { mitk::Annotation::Bounds bounds; bounds.Position[0] = bounds.Position[1] = bounds.Size[0] = bounds.Size[1] = 0; return bounds; } void mitk::Annotation::SetBoundsOnDisplay(mitk::BaseRenderer *, const mitk::Annotation::Bounds &) { } void mitk::Annotation::SetForceInForeground(bool forceForeground) { m_ForceInForeground = forceForeground; } bool mitk::Annotation::IsForceInForeground() const { return m_ForceInForeground; } mitk::PropertyList *mitk::Annotation::GetPropertyList() const { return m_PropertyList; } std::string mitk::Annotation::GetMicroserviceID() { return this->m_ServiceRegistration.GetReference().GetProperty(US_PROPKEY_ID).ToString(); } void mitk::Annotation::RegisterAsMicroservice(us::ServiceProperties props) { if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); us::ModuleContext *context = us::GetModuleContext(); // Define ServiceProps - mitk::UIDGenerator uidGen = mitk::UIDGenerator("org.mitk.services.Annotation.id_", 16); + mitk::UIDGenerator uidGen = mitk::UIDGenerator("org.mitk.services.Annotation.id_"); props[US_PROPKEY_ID] = uidGen.GetUID(); m_ServiceRegistration = context->RegisterService(this, props); } void mitk::Annotation::UnRegisterMicroservice() { if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } void mitk::Annotation::AnnotationModified() { Modified(); this->SetUSProperty(US_PROPKEY_MODIFIED, this->GetMTime()); } diff --git a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp index cdb8d2c0a3..ee62b659cd 100644 --- a/Modules/Core/src/Rendering/mitkBaseRenderer.cpp +++ b/Modules/Core/src/Rendering/mitkBaseRenderer.cpp @@ -1,775 +1,784 @@ /*============================================================================ 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 "mitkBaseRenderer.h" #include "mitkMapper.h" #include "mitkResliceMethodProperty.h" // Geometries #include "mitkPlaneGeometry.h" #include "mitkSlicedGeometry3D.h" // Controllers #include "mitkCameraController.h" #include "mitkCameraRotationController.h" #include "mitkSliceNavigationController.h" #include "mitkVtkLayerController.h" #include "mitkInteractionConst.h" #include "mitkProperties.h" #include "mitkWeakPointerProperty.h" // VTK #include #include #include #include #include #include #include mitk::BaseRenderer::BaseRendererMapType mitk::BaseRenderer::baseRendererMap; mitk::BaseRenderer *mitk::BaseRenderer::GetInstance(vtkRenderWindow *renWin) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).first == renWin) return (*mapit).second; } return nullptr; } void mitk::BaseRenderer::AddInstance(vtkRenderWindow *renWin, BaseRenderer *baseRenderer) { if (renWin == nullptr || baseRenderer == nullptr) return; // ensure that no BaseRenderer is managed twice mitk::BaseRenderer::RemoveInstance(renWin); baseRendererMap.insert(BaseRendererMapType::value_type(renWin, baseRenderer)); } void mitk::BaseRenderer::RemoveInstance(vtkRenderWindow *renWin) { auto mapit = baseRendererMap.find(renWin); if (mapit != baseRendererMap.end()) baseRendererMap.erase(mapit); } mitk::BaseRenderer *mitk::BaseRenderer::GetByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).second; } return nullptr; } vtkRenderWindow *mitk::BaseRenderer::GetRenderWindowByName(const std::string &name) { for (auto mapit = baseRendererMap.begin(); mapit != baseRendererMap.end(); ++mapit) { if ((*mapit).second->m_Name == name) return (*mapit).first; } return nullptr; } mitk::BaseRenderer::BaseRenderer(const char *name, vtkRenderWindow *renWin) : m_RenderWindow(nullptr), m_VtkRenderer(nullptr), m_MapperID(defaultMapper), m_DataStorage(nullptr), m_LastUpdateTime(0), m_CameraController(nullptr), m_SliceNavigationController(nullptr), m_CameraRotationController(nullptr), m_WorldTimeGeometry(nullptr), m_CurrentWorldGeometry(nullptr), m_CurrentWorldPlaneGeometry(nullptr), m_Slice(0), m_TimeStep(), m_CurrentWorldPlaneGeometryUpdateTime(), m_TimeStepUpdateTime(), m_KeepDisplayedRegion(true), m_CurrentWorldPlaneGeometryData(nullptr), m_CurrentWorldPlaneGeometryNode(nullptr), m_CurrentWorldPlaneGeometryTransformTime(0), m_Name(name), m_EmptyWorldGeometry(true), m_NumberOfVisibleLODEnabledMappers(0) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; if (name != nullptr) { m_Name = name; } else { m_Name = "unnamed renderer"; itkWarningMacro(<< "Created unnamed renderer. Bad for serialization. Please choose a name."); } if (renWin != nullptr) { m_RenderWindow = renWin; m_RenderWindow->Register(nullptr); } else { itkWarningMacro(<< "Created mitkBaseRenderer without vtkRenderWindow present."); } // instances.insert( this ); // adding this BaseRenderer to the List of all BaseRenderer m_BindDispatcherInteractor = new mitk::BindDispatcherInteractor(GetName()); WeakPointerProperty::Pointer rendererProp = WeakPointerProperty::New((itk::Object *)this); m_CurrentWorldPlaneGeometry = mitk::PlaneGeometry::New(); m_CurrentWorldPlaneGeometryData = mitk::PlaneGeometryData::New(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryNode = mitk::DataNode::New(); m_CurrentWorldPlaneGeometryNode->SetData(m_CurrentWorldPlaneGeometryData); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("renderer", rendererProp); m_CurrentWorldPlaneGeometryNode->GetPropertyList()->SetProperty("layer", IntProperty::New(1000)); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices", mitk::ResliceMethodProperty::New()); m_CurrentWorldPlaneGeometryNode->SetProperty("reslice.thickslices.num", mitk::IntProperty::New(1)); m_CurrentWorldPlaneGeometryTransformTime = m_CurrentWorldPlaneGeometryNode->GetVtkTransform()->GetMTime(); mitk::SliceNavigationController::Pointer sliceNavigationController = mitk::SliceNavigationController::New(); sliceNavigationController->SetRenderer(this); sliceNavigationController->ConnectGeometrySliceEvent(this); sliceNavigationController->ConnectGeometryUpdateEvent(this); sliceNavigationController->ConnectGeometryTimeEvent(this, false); m_SliceNavigationController = sliceNavigationController; m_CameraRotationController = mitk::CameraRotationController::New(); m_CameraRotationController->SetRenderWindow(m_RenderWindow); m_CameraRotationController->AcquireCamera(); m_CameraController = mitk::CameraController::New(); m_CameraController->SetRenderer(this); m_VtkRenderer = vtkRenderer::New(); - - m_VtkRenderer->UseDepthPeelingOn(); - m_VtkRenderer->UseDepthPeelingForVolumesOn(); - m_VtkRenderer->SetMaximumNumberOfPeels(16); // This could be made adjustable in the Preferences - m_VtkRenderer->SetOcclusionRatio(0.0); + m_VtkRenderer->SetMaximumNumberOfPeels(16); if (AntiAliasing::FastApproximate == RenderingManager::GetInstance()->GetAntiAliasing()) m_VtkRenderer->UseFXAAOn(); if (nullptr == mitk::VtkLayerController::GetInstance(m_RenderWindow)) mitk::VtkLayerController::AddInstance(m_RenderWindow, m_VtkRenderer); mitk::VtkLayerController::GetInstance(m_RenderWindow)->InsertSceneRenderer(m_VtkRenderer); } mitk::BaseRenderer::~BaseRenderer() { if (m_VtkRenderer != nullptr) { m_VtkRenderer->Delete(); m_VtkRenderer = nullptr; } if (m_CameraController.IsNotNull()) m_CameraController->SetRenderer(nullptr); mitk::VtkLayerController::RemoveInstance(m_RenderWindow); RemoveAllLocalStorages(); m_DataStorage = nullptr; if (m_BindDispatcherInteractor != nullptr) { delete m_BindDispatcherInteractor; } if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); m_RenderWindow = nullptr; } } +void mitk::BaseRenderer::SetMapperID(MapperSlotId id) +{ + if (m_MapperID != id) + { + bool useDepthPeeling = Standard3D == id; + m_VtkRenderer->SetUseDepthPeeling(useDepthPeeling); + m_VtkRenderer->SetUseDepthPeelingForVolumes(useDepthPeeling); + + m_MapperID = id; + this->Modified(); + } +} + void mitk::BaseRenderer::RemoveAllLocalStorages() { this->InvokeEvent(mitk::BaseRenderer::RendererResetEvent()); std::list::iterator it; for (it = m_RegisteredLocalStorageHandlers.begin(); it != m_RegisteredLocalStorageHandlers.end(); ++it) (*it)->ClearLocalStorage(this, false); m_RegisteredLocalStorageHandlers.clear(); } void mitk::BaseRenderer::RegisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.push_back(lsh); } mitk::Dispatcher::Pointer mitk::BaseRenderer::GetDispatcher() const { return m_BindDispatcherInteractor->GetDispatcher(); } void mitk::BaseRenderer::UnregisterLocalStorageHandler(mitk::BaseLocalStorageHandler *lsh) { m_RegisteredLocalStorageHandlers.remove(lsh); } void mitk::BaseRenderer::SetDataStorage(DataStorage *storage) { if (storage != m_DataStorage && storage != nullptr) { m_DataStorage = storage; m_BindDispatcherInteractor->SetDataStorage(m_DataStorage); this->Modified(); } } const mitk::BaseRenderer::MapperSlotId mitk::BaseRenderer::defaultMapper = 1; void mitk::BaseRenderer::Paint() { } void mitk::BaseRenderer::Initialize() { } void mitk::BaseRenderer::Resize(int w, int h) { this->m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::InitRenderer(vtkRenderWindow *renderwindow) { if (m_RenderWindow != renderwindow) { if (m_RenderWindow != nullptr) { m_RenderWindow->Delete(); } m_RenderWindow = renderwindow; if (m_RenderWindow != nullptr) { m_RenderWindow->Register(nullptr); } } RemoveAllLocalStorages(); if (m_CameraController.IsNotNull()) { m_CameraController->SetRenderer(this); } } void mitk::BaseRenderer::InitSize(int w, int h) { this->m_RenderWindow->SetSize(w, h); } void mitk::BaseRenderer::SetSlice(unsigned int slice) { if (m_Slice != slice) { m_Slice = slice; if (m_WorldTimeGeometry.IsNotNull()) { // get world geometry which may be rotated, for the current time step SlicedGeometry3D *slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { // if slice position is part of the world geometry... if (m_Slice >= slicedWorldGeometry->GetSlices()) // set the current worldplanegeomety as the selected 2D slice of the world geometry m_Slice = slicedWorldGeometry->GetSlices() - 1; SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } void mitk::BaseRenderer::SetTimeStep(unsigned int timeStep) { if (m_TimeStep != timeStep) { m_TimeStep = timeStep; m_TimeStepUpdateTime.Modified(); if (m_WorldTimeGeometry.IsNotNull()) { if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; SlicedGeometry3D *slicedWorldGeometry = dynamic_cast(m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep).GetPointer()); if (slicedWorldGeometry != nullptr) { SetCurrentWorldPlaneGeometry(slicedWorldGeometry->GetPlaneGeometry(m_Slice)); SetCurrentWorldGeometry(slicedWorldGeometry); } } else Modified(); } } int mitk::BaseRenderer::GetTimeStep(const mitk::BaseData *data) const { if ((data == nullptr) || (data->IsInitialized() == false)) { return -1; } return data->GetTimeGeometry()->TimePointToTimeStep(GetTime()); } mitk::ScalarType mitk::BaseRenderer::GetTime() const { if (m_WorldTimeGeometry.IsNull()) { return 0; } else { ScalarType timeInMS = m_WorldTimeGeometry->TimeStepToTimePoint(GetTimeStep()); if (timeInMS == itk::NumericTraits::NonpositiveMin()) return 0; else return timeInMS; } } void mitk::BaseRenderer::SetWorldTimeGeometry(const mitk::TimeGeometry *geometry) { assert(geometry != nullptr); itkDebugMacro("setting WorldTimeGeometry to " << geometry); if (m_WorldTimeGeometry != geometry) { if (geometry->GetBoundingBoxInWorld()->GetDiagonalLength2() == 0) return; m_WorldTimeGeometry = geometry; itkDebugMacro("setting WorldTimeGeometry to " << m_WorldTimeGeometry); if (m_TimeStep >= m_WorldTimeGeometry->CountTimeSteps()) m_TimeStep = m_WorldTimeGeometry->CountTimeSteps() - 1; BaseGeometry *geometry3d; geometry3d = m_WorldTimeGeometry->GetGeometryForTimeStep(m_TimeStep); SetWorldGeometry3D(geometry3d); } } void mitk::BaseRenderer::SetWorldGeometry3D(const mitk::BaseGeometry *geometry) { itkDebugMacro("setting WorldGeometry3D to " << geometry); if (geometry->GetBoundingBox()->GetDiagonalLength2() == 0) return; const SlicedGeometry3D *slicedWorldGeometry; slicedWorldGeometry = dynamic_cast(geometry); PlaneGeometry::ConstPointer geometry2d; if (slicedWorldGeometry != nullptr) { if (m_Slice >= slicedWorldGeometry->GetSlices() && (m_Slice != 0)) m_Slice = slicedWorldGeometry->GetSlices() - 1; geometry2d = slicedWorldGeometry->GetPlaneGeometry(m_Slice); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = mitk::PlaneGeometry::New(); plane->InitializeStandardPlane(slicedWorldGeometry); geometry2d = plane; } SetCurrentWorldGeometry(slicedWorldGeometry); } else { geometry2d = dynamic_cast(geometry); if (geometry2d.IsNull()) { PlaneGeometry::Pointer plane = PlaneGeometry::New(); plane->InitializeStandardPlane(geometry); geometry2d = plane; } SetCurrentWorldGeometry(geometry); } SetCurrentWorldPlaneGeometry(geometry2d); // calls Modified() if (m_CurrentWorldPlaneGeometry.IsNull()) itkWarningMacro("m_CurrentWorldPlaneGeometry is nullptr"); } void mitk::BaseRenderer::SetCurrentWorldPlaneGeometry(const mitk::PlaneGeometry *geometry2d) { if (m_CurrentWorldPlaneGeometry != geometry2d) { m_CurrentWorldPlaneGeometry = geometry2d->Clone(); m_CurrentWorldPlaneGeometryData->SetPlaneGeometry(m_CurrentWorldPlaneGeometry); m_CurrentWorldPlaneGeometryUpdateTime.Modified(); Modified(); } } void mitk::BaseRenderer::SendUpdateSlice() { m_CurrentWorldPlaneGeometryUpdateTime.Modified(); } int *mitk::BaseRenderer::GetSize() const { return this->m_RenderWindow->GetSize(); } int *mitk::BaseRenderer::GetViewportSize() const { return this->m_VtkRenderer->GetSize(); } void mitk::BaseRenderer::SetCurrentWorldGeometry(const mitk::BaseGeometry *geometry) { m_CurrentWorldGeometry = geometry; if (geometry == nullptr) { m_Bounds[0] = 0; m_Bounds[1] = 0; m_Bounds[2] = 0; m_Bounds[3] = 0; m_Bounds[4] = 0; m_Bounds[5] = 0; m_EmptyWorldGeometry = true; return; } BoundingBox::Pointer boundingBox = m_CurrentWorldGeometry->CalculateBoundingBoxRelativeToTransform(nullptr); const BoundingBox::BoundsArrayType &worldBounds = boundingBox->GetBounds(); m_Bounds[0] = worldBounds[0]; m_Bounds[1] = worldBounds[1]; m_Bounds[2] = worldBounds[2]; m_Bounds[3] = worldBounds[3]; m_Bounds[4] = worldBounds[4]; m_Bounds[5] = worldBounds[5]; if (boundingBox->GetDiagonalLength2() <= mitk::eps) m_EmptyWorldGeometry = true; else m_EmptyWorldGeometry = false; } void mitk::BaseRenderer::SetGeometry(const itk::EventObject &geometrySendEvent) { const auto *sendEvent = dynamic_cast(&geometrySendEvent); assert(sendEvent != nullptr); SetWorldTimeGeometry(sendEvent->GetTimeGeometry()); } void mitk::BaseRenderer::UpdateGeometry(const itk::EventObject &geometryUpdateEvent) { const auto *updateEvent = dynamic_cast(&geometryUpdateEvent); if (updateEvent == nullptr) return; if (m_CurrentWorldGeometry.IsNotNull()) { auto *slicedWorldGeometry = dynamic_cast(m_CurrentWorldGeometry.GetPointer()); if (slicedWorldGeometry) { PlaneGeometry *geometry2D = slicedWorldGeometry->GetPlaneGeometry(m_Slice); SetCurrentWorldPlaneGeometry(geometry2D); // calls Modified() } } } void mitk::BaseRenderer::SetGeometrySlice(const itk::EventObject &geometrySliceEvent) { const auto *sliceEvent = dynamic_cast(&geometrySliceEvent); assert(sliceEvent != nullptr); SetSlice(sliceEvent->GetPos()); } void mitk::BaseRenderer::SetGeometryTime(const itk::EventObject &geometryTimeEvent) { const auto *timeEvent = dynamic_cast(&geometryTimeEvent); assert(timeEvent != nullptr); SetTimeStep(timeEvent->GetPos()); } const double *mitk::BaseRenderer::GetBounds() const { return m_Bounds; } void mitk::BaseRenderer::DrawOverlayMouse(mitk::Point2D &itkNotUsed(p2d)) { MITK_INFO << "BaseRenderer::DrawOverlayMouse()- should be inconcret implementation OpenGLRenderer." << std::endl; } void mitk::BaseRenderer::RequestUpdate() { SetConstrainZoomingAndPanning(true); RenderingManager::GetInstance()->RequestUpdate(this->m_RenderWindow); } void mitk::BaseRenderer::ForceImmediateUpdate() { RenderingManager::GetInstance()->ForceImmediateUpdate(this->m_RenderWindow); } unsigned int mitk::BaseRenderer::GetNumberOfVisibleLODEnabledMappers() const { return m_NumberOfVisibleLODEnabledMappers; } /*! Sets the new Navigation controller */ void mitk::BaseRenderer::SetSliceNavigationController(mitk::SliceNavigationController *SlicenavigationController) { if (SlicenavigationController == nullptr) return; // copy worldgeometry SlicenavigationController->SetInputWorldTimeGeometry(SlicenavigationController->GetCreatedWorldGeometry()); SlicenavigationController->Update(); // set new m_SliceNavigationController = SlicenavigationController; m_SliceNavigationController->SetRenderer(this); if (m_SliceNavigationController.IsNotNull()) { m_SliceNavigationController->ConnectGeometrySliceEvent(this); m_SliceNavigationController->ConnectGeometryUpdateEvent(this); m_SliceNavigationController->ConnectGeometryTimeEvent(this, false); } } void mitk::BaseRenderer::DisplayToWorld(const Point2D &displayPoint, Point3D &worldIndex) const { if (m_MapperID == BaseRenderer::Standard2D) { double display[3], *world; // For the rigth z-position in display coordinates, take the focal point, convert it to display and use it for // correct depth. double *displayCoord; double cameraFP[4]; // Get camera focal point and position. Convert to display (screen) // coordinates. We need a depth value for z-buffer. this->GetVtkRenderer()->GetActiveCamera()->GetFocalPoint(cameraFP); cameraFP[3] = 0.0; this->GetVtkRenderer()->SetWorldPoint(cameraFP[0], cameraFP[1], cameraFP[2], cameraFP[3]); this->GetVtkRenderer()->WorldToDisplay(); displayCoord = this->GetVtkRenderer()->GetDisplayPoint(); // now convert the display point to world coordinates display[0] = displayPoint[0]; display[1] = displayPoint[1]; display[2] = displayCoord[2]; this->GetVtkRenderer()->SetDisplayPoint(display); this->GetVtkRenderer()->DisplayToWorld(); world = this->GetVtkRenderer()->GetWorldPoint(); for (int i = 0; i < 3; i++) { worldIndex[i] = world[i] / world[3]; } } else if (m_MapperID == BaseRenderer::Standard3D) { PickWorldPoint( displayPoint, worldIndex); // Seems to be the same code as above, but subclasses may contain different implementations. } return; } void mitk::BaseRenderer::DisplayToPlane(const Point2D &displayPoint, Point2D &planePointInMM) const { if (m_MapperID == BaseRenderer::Standard2D) { Point3D worldPoint; this->DisplayToWorld(displayPoint, worldPoint); this->m_CurrentWorldPlaneGeometry->Map(worldPoint, planePointInMM); } else if (m_MapperID == BaseRenderer::Standard3D) { MITK_WARN << "No conversion possible with 3D mapper."; return; } return; } void mitk::BaseRenderer::WorldToDisplay(const Point3D &worldIndex, Point2D &displayPoint) const { double world[4], *display; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToDisplay(); display = this->GetVtkRenderer()->GetDisplayPoint(); displayPoint[0] = display[0]; displayPoint[1] = display[1]; return; } void mitk::BaseRenderer::WorldToView(const mitk::Point3D &worldIndex, mitk::Point2D &viewPoint) const { double world[4], *view; world[0] = worldIndex[0]; world[1] = worldIndex[1]; world[2] = worldIndex[2]; world[3] = 1.0; this->GetVtkRenderer()->SetWorldPoint(world); this->GetVtkRenderer()->WorldToView(); view = this->GetVtkRenderer()->GetViewPoint(); this->GetVtkRenderer()->ViewToNormalizedViewport(view[0], view[1], view[2]); viewPoint[0] = view[0] * this->GetViewportSize()[0]; viewPoint[1] = view[1] * this->GetViewportSize()[1]; return; } void mitk::BaseRenderer::PlaneToDisplay(const Point2D &planePointInMM, Point2D &displayPoint) const { Point3D worldPoint; this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToDisplay(worldPoint, displayPoint); return; } void mitk::BaseRenderer::PlaneToView(const Point2D &planePointInMM, Point2D &viewPoint) const { Point3D worldPoint; this->m_CurrentWorldPlaneGeometry->Map(planePointInMM, worldPoint); this->WorldToView(worldPoint,viewPoint); return; } double mitk::BaseRenderer::GetScaleFactorMMPerDisplayUnit() const { if (this->GetMapperID() == BaseRenderer::Standard2D) { // GetParallelScale returns half of the height of the render window in mm. // Divided by the half size of the Display size in pixel givest the mm per pixel. return this->GetVtkRenderer()->GetActiveCamera()->GetParallelScale() * 2.0 / GetViewportSize()[1]; } else return 1.0; } mitk::Point2D mitk::BaseRenderer::GetDisplaySizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetSizeX() * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetSizeY() * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetViewportSizeInMM() const { Point2D dispSizeInMM; dispSizeInMM[0] = GetViewportSize()[0] * GetScaleFactorMMPerDisplayUnit(); dispSizeInMM[1] = GetViewportSize()[1] * GetScaleFactorMMPerDisplayUnit(); return dispSizeInMM; } mitk::Point2D mitk::BaseRenderer::GetOriginInMM() const { Point2D originPx; originPx[0] = m_VtkRenderer->GetOrigin()[0]; originPx[1] = m_VtkRenderer->GetOrigin()[1]; Point2D displayGeometryOriginInMM; DisplayToPlane(originPx, displayGeometryOriginInMM); // top left of the render window (Origin) return displayGeometryOriginInMM; } void mitk::BaseRenderer::SetConstrainZoomingAndPanning(bool constrain) { m_ConstrainZoomingAndPanning = constrain; if (m_ConstrainZoomingAndPanning) { this->GetCameraController()->AdjustCameraToPlane(); } } mitk::Point3D mitk::BaseRenderer::Map2DRendererPositionTo3DWorldPosition(const Point2D &mousePosition) const { // DEPRECATED: Map2DRendererPositionTo3DWorldPosition is deprecated. use DisplayToWorldInstead Point3D position; DisplayToWorld(mousePosition, position); return position; } void mitk::BaseRenderer::PrintSelf(std::ostream &os, itk::Indent indent) const { os << indent << " MapperID: " << m_MapperID << std::endl; os << indent << " Slice: " << m_Slice << std::endl; os << indent << " TimeStep: " << m_TimeStep << std::endl; os << indent << " CurrentWorldPlaneGeometry: "; if (m_CurrentWorldPlaneGeometry.IsNull()) os << "nullptr" << std::endl; else m_CurrentWorldPlaneGeometry->Print(os, indent); os << indent << " CurrentWorldPlaneGeometryUpdateTime: " << m_CurrentWorldPlaneGeometryUpdateTime << std::endl; os << indent << " CurrentWorldPlaneGeometryTransformTime: " << m_CurrentWorldPlaneGeometryTransformTime << std::endl; Superclass::PrintSelf(os, indent); } diff --git a/Modules/Core/test/files.cmake b/Modules/Core/test/files.cmake index 5efc282bfa..63b574f8ed 100644 --- a/Modules/Core/test/files.cmake +++ b/Modules/Core/test/files.cmake @@ -1,194 +1,195 @@ # tests with no extra command line parameter set(MODULE_TESTS # IMPORTANT: If you plan to deactivate / comment out a test please write a bug number to the commented out line of code. # # Example: #mitkMyTest #this test is commented out because of bug 12345 # # It is important that the bug is open and that the test will be activated again before the bug is closed. This assures that # no test is forgotten after it was commented out. If there is no bug for your current problem, please add a new one and # mark it as critical. ################## DISABLED TESTS ################################################# #mitkAbstractTransformGeometryTest.cpp #seems as tested class mitkExternAbstractTransformGeometry doesnt exist any more #mitkStateMachineContainerTest.cpp #rewrite test, indirect since no longer exported Bug 14529 #mitkRegistrationBaseTest.cpp #tested class mitkRegistrationBase doesn't exist any more #mitkSegmentationInterpolationTest.cpp #file doesn't exist! #mitkPipelineSmartPointerCorrectnessTest.cpp #file doesn't exist! #mitkITKThreadingTest.cpp #test outdated because itk::Semaphore was removed from ITK #mitkAbstractTransformPlaneGeometryTest.cpp #mitkVtkAbstractTransformPlaneGeometry doesn't exist any more #mitkTestUtilSharedLibrary.cpp #Linker problem with this test... #mitkTextOverlay2DSymbolsRenderingTest.cpp #Implementation of the tested feature is not finished yet. Ask Christoph or see bug 15104 for details. ################# RUNNING TESTS ################################################### mitkAccessByItkTest.cpp mitkCoreObjectFactoryTest.cpp mitkDataNodeTest.cpp mitkMaterialTest.cpp mitkActionTest.cpp mitkDispatcherTest.cpp mitkEnumerationPropertyTest.cpp mitkFileReaderRegistryTest.cpp #mitkFileWriterRegistryTest.cpp mitkFloatToStringTest.cpp mitkGenericPropertyTest.cpp mitkGeometry3DTest.cpp mitkGeometry3DEqualTest.cpp mitkGeometryDataIOTest.cpp mitkGeometryDataToSurfaceFilterTest.cpp mitkImageCastTest.cpp mitkImageDataItemTest.cpp mitkImageGeneratorTest.cpp mitkIOUtilTest.cpp mitkBaseDataTest.cpp mitkImportItkImageTest.cpp mitkGrabItkImageMemoryTest.cpp mitkInstantiateAccessFunctionTest.cpp mitkLevelWindowTest.cpp mitkMessageTest.cpp mitkPixelTypeTest.cpp mitkPlaneGeometryTest.cpp mitkPointSetTest.cpp mitkPointSetEqualTest.cpp mitkPointSetFileIOTest.cpp mitkPointSetOnEmptyTest.cpp mitkPointSetLocaleTest.cpp mitkPointSetWriterTest.cpp mitkPointSetPointOperationsTest.cpp mitkProgressBarTest.cpp mitkPropertyTest.cpp mitkPropertyListTest.cpp mitkPropertyPersistenceTest.cpp mitkPropertyPersistenceInfoTest.cpp mitkPropertyRelationRuleBaseTest.cpp mitkPropertyRelationsTest.cpp mitkSlicedGeometry3DTest.cpp mitkSliceNavigationControllerTest.cpp mitkSurfaceTest.cpp mitkSurfaceEqualTest.cpp mitkSurfaceToSurfaceFilterTest.cpp mitkTimeGeometryTest.cpp mitkProportionalTimeGeometryTest.cpp mitkUndoControllerTest.cpp mitkVtkWidgetRenderingTest.cpp mitkVerboseLimitedLinearUndoTest.cpp mitkWeakPointerTest.cpp mitkTransferFunctionTest.cpp mitkStepperTest.cpp mitkRenderingManagerTest.cpp mitkCompositePixelValueToStringTest.cpp vtkMitkThickSlicesFilterTest.cpp mitkNodePredicateSourceTest.cpp mitkNodePredicateDataPropertyTest.cpp mitkNodePredicateFunctionTest.cpp mitkVectorTest.cpp mitkClippedSurfaceBoundsCalculatorTest.cpp mitkExceptionTest.cpp mitkExtractSliceFilterTest.cpp mitkLogTest.cpp mitkImageDimensionConverterTest.cpp mitkLoggingAdapterTest.cpp mitkUIDGeneratorTest.cpp mitkPlanePositionManagerTest.cpp mitkAffineTransformBaseTest.cpp mitkPropertyAliasesTest.cpp mitkPropertyDescriptionsTest.cpp mitkPropertyExtensionsTest.cpp mitkPropertyFiltersTest.cpp mitkPropertyKeyPathTest.cpp mitkTinyXMLTest.cpp mitkRawImageFileReaderTest.cpp mitkInteractionEventTest.cpp mitkLookupTableTest.cpp mitkSTLFileReaderTest.cpp mitkPointTypeConversionTest.cpp mitkVectorTypeConversionTest.cpp mitkMatrixTypeConversionTest.cpp mitkArrayTypeConversionTest.cpp mitkSurfaceToImageFilterTest.cpp mitkBaseGeometryTest.cpp mitkImageToSurfaceFilterTest.cpp mitkEqualTest.cpp mitkLineTest.cpp mitkArbitraryTimeGeometryTest.cpp mitkItkImageIOTest.cpp mitkLevelWindowManagerCppUnitTest.cpp mitkVectorPropertyTest.cpp mitkTemporoSpatialStringPropertyTest.cpp mitkPropertyNameHelperTest.cpp mitkNodePredicateGeometryTest.cpp + mitkNodePredicateSubGeometryTest.cpp mitkPreferenceListReaderOptionsFunctorTest.cpp mitkGenericIDRelationRuleTest.cpp mitkSourceImageRelationRuleTest.cpp mitkPointSetDataInteractorTest.cpp #since mitkInteractionTestHelper is currently creating a vtkRenderWindow mitkSurfaceVtkMapper2DTest.cpp #new rendering test in CppUnit style mitkSurfaceVtkMapper2D3DTest.cpp # comparisons/consistency 2D/3D mitkTemporalJoinImagesFilterTest.cpp ) # test with image filename as an extra command line parameter set(MODULE_IMAGE_TESTS mitkImageTimeSelectorTest.cpp #only runs on images mitkImageAccessorTest.cpp #only runs on images ) set(MODULE_SURFACE_TESTS mitkSurfaceVtkWriterTest.cpp #only runs on surfaces ) # list of images for which the tests are run set(MODULE_TESTIMAGE US4DCyl.nrrd Pic3D.nrrd Pic2DplusT.nrrd BallBinary30x30x30.nrrd Png2D-bw.png ) set(MODULE_TESTSURFACE binary.stl ball.stl ) set(MODULE_CUSTOM_TESTS mitkDataStorageTest.cpp mitkDataNodeTest.cpp mitkEventConfigTest.cpp mitkPointSetLocaleTest.cpp mitkImageTest.cpp mitkImageVtkMapper2DTest.cpp mitkImageVtkMapper2DLevelWindowTest.cpp mitkImageVtkMapper2DOpacityTest.cpp mitkImageVtkMapper2DResliceInterpolationPropertyTest.cpp mitkImageVtkMapper2DColorTest.cpp mitkImageVtkMapper2DSwivelTest.cpp mitkImageVtkMapper2DTransferFunctionTest.cpp mitkImageVtkMapper2DOpacityTransferFunctionTest.cpp mitkImageVtkMapper2DLookupTableTest.cpp mitkSurfaceVtkMapper3DTest.cpp mitkVolumeCalculatorTest.cpp mitkLevelWindowManagerTest.cpp mitkPointSetVtkMapper2DTest.cpp mitkPointSetVtkMapper2DImageTest.cpp mitkPointSetVtkMapper2DGlyphTypeTest.cpp mitkPointSetVtkMapper2DTransformedPointsTest.cpp mitkVTKRenderWindowSizeTest.cpp mitkMultiComponentImageDataComparisonFilterTest.cpp mitkImageToItkTest.cpp mitkImageSliceSelectorTest.cpp mitkPointSetReaderTest.cpp mitkImageEqualTest.cpp mitkRotatedSlice4DTest.cpp mitkPlaneGeometryDataMapper2DTest.cpp ) # Currently not working on windows because of a rendering timing issue # see bug 18083 for details if(NOT WIN32) set(MODULE_CUSTOM_TESTS ${MODULE_CUSTOM_TESTS} mitkSurfaceDepthSortingTest.cpp) endif() set(RESOURCE_FILES Interactions/AddAndRemovePoints.xml Interactions/globalConfig.xml Interactions/StatemachineTest.xml Interactions/StatemachineConfigTest.xml ) diff --git a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp index f25f0e4377..b05b96b04c 100644 --- a/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp +++ b/Modules/Core/test/mitkArbitraryTimeGeometryTest.cpp @@ -1,465 +1,504 @@ /*============================================================================ 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 "mitkArbitraryTimeGeometry.h" #include "mitkGeometry3D.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkArbitraryTimeGeometryTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkArbitraryTimeGeometryTestSuite); // Test the append method MITK_TEST(CountTimeSteps); MITK_TEST(GetMinimumTimePoint); MITK_TEST(GetMaximumTimePoint); MITK_TEST(GetTimeBounds); MITK_TEST(IsValidTimePoint); MITK_TEST(TimeStepToTimePoint); MITK_TEST(TimePointToTimeStep); MITK_TEST(GetGeometryCloneForTimeStep); MITK_TEST(GetGeometryForTimeStep); MITK_TEST(GetGeometryForTimePoint); MITK_TEST(IsValid); MITK_TEST(Expand); MITK_TEST(ReplaceTimeStepGeometries); MITK_TEST(ClearAllGeometries); MITK_TEST(AppendNewTimeStep); + MITK_TEST(HasCollapsedFinalTimeStep); CPPUNIT_TEST_SUITE_END(); private: mitk::Geometry3D::Pointer m_Geometry1; mitk::Geometry3D::Pointer m_Geometry2; mitk::Geometry3D::Pointer m_Geometry3; mitk::Geometry3D::Pointer m_Geometry3_5; mitk::Geometry3D::Pointer m_Geometry4; mitk::Geometry3D::Pointer m_Geometry5; mitk::Geometry3D::Pointer m_InvalidGeometry; mitk::Geometry3D::Pointer m_NewGeometry; mitk::TimePointType m_Geometry1MinTP; mitk::TimePointType m_Geometry2MinTP; mitk::TimePointType m_Geometry3MinTP; mitk::TimePointType m_Geometry3_5MinTP; mitk::TimePointType m_Geometry4MinTP; mitk::TimePointType m_Geometry5MinTP; mitk::TimePointType m_NewGeometryMinTP; mitk::TimePointType m_Geometry1MaxTP; mitk::TimePointType m_Geometry2MaxTP; mitk::TimePointType m_Geometry3MaxTP; mitk::TimePointType m_Geometry3_5MaxTP; mitk::TimePointType m_Geometry4MaxTP; mitk::TimePointType m_Geometry5MaxTP; mitk::TimePointType m_NewGeometryMaxTP; mitk::ArbitraryTimeGeometry::Pointer m_emptyTimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_initTimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_12345TimeGeometry; mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometry; + mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometryWithCollapsedEnd; + mitk::ArbitraryTimeGeometry::Pointer m_123TimeGeometryWithCollapsedInterim; public: void setUp() override { mitk::TimeBounds bounds; m_Geometry1 = mitk::Geometry3D::New(); m_Geometry2 = mitk::Geometry3D::New(); m_Geometry3 = mitk::Geometry3D::New(); m_Geometry3_5 = mitk::Geometry3D::New(); m_Geometry4 = mitk::Geometry3D::New(); m_Geometry5 = mitk::Geometry3D::New(); m_Geometry1MinTP = 1; m_Geometry2MinTP = 2; m_Geometry3MinTP = 3; m_Geometry3_5MinTP = 3.5; m_Geometry4MinTP = 4; m_Geometry5MinTP = 5; m_Geometry1MaxTP = 1.9; m_Geometry2MaxTP = 2.9; m_Geometry3MaxTP = 3.9; m_Geometry3_5MaxTP = 3.9; m_Geometry4MaxTP = 4.9; m_Geometry5MaxTP = 5.9; m_NewGeometry = mitk::Geometry3D::New(); m_NewGeometryMinTP = 20; m_NewGeometryMaxTP = 21.9; mitk::Point3D origin(42); m_NewGeometry->SetOrigin(origin); m_emptyTimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_emptyTimeGeometry->ClearAllGeometries(); m_initTimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_initTimeGeometry->Initialize(); m_12345TimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_12345TimeGeometry->ClearAllGeometries(); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); m_12345TimeGeometry->AppendNewTimeStep(m_Geometry5, m_Geometry5MinTP, m_Geometry5MaxTP); m_123TimeGeometry = mitk::ArbitraryTimeGeometry::New(); m_123TimeGeometry->ClearAllGeometries(); m_123TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); m_123TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); m_123TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); + + m_123TimeGeometryWithCollapsedEnd = mitk::ArbitraryTimeGeometry::New(); + m_123TimeGeometryWithCollapsedEnd->ClearAllGeometries(); + m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); + m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); + m_123TimeGeometryWithCollapsedEnd->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MinTP); + + m_123TimeGeometryWithCollapsedInterim = mitk::ArbitraryTimeGeometry::New(); + m_123TimeGeometryWithCollapsedInterim->ClearAllGeometries(); + m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); + m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MinTP); + m_123TimeGeometryWithCollapsedInterim->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); } void tearDown() override {} void CountTimeSteps() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 0, "Testing CountTimeSteps with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->CountTimeSteps() == 1, "Testing CountTimeSteps with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing CountTimeSteps with m_12345TimeGeometry"); } void GetMinimumTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 0.0, "Testing GetMinimumTimePoint with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint() == 0.0, "Testing GetMinimumTimePoint with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 1.0, "Testing GetMinimumTimePoint with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint(2) == 0.0, "Testing GetMinimumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMinimumTimePoint(2) == 0.0, "Testing GetMinimumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint(2) == 2.9, "Testing GetMinimumTimePoint(2) with m_12345TimeGeometry"); } void GetMaximumTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 0.0, "Testing GetMaximumTimePoint with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint() == 1.0, "Testing GetMaximumTimePoint with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 5.9, "Testing GetMaximumTimePoint with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9, "Testing GetMaximumTimePoint(2) with m_12345TimeGeometry"); } void GetTimeBounds() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetMaximumTimePoint(2) == 0.0, "Testing GetMaximumTimePoint(2) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint(2) == 3.9, "Testing GetMaximumTimePoint(2) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[0] == 0.0, "Testing GetTimeBounds lower part with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[0] == 0.0, "Testing GetTimeBounds lower part with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[0] == 1.0, "Testing GetTimeBounds lower part with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds()[1] == 0.0, "Testing GetTimeBounds with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds()[1] == 1.0, "Testing GetTimeBounds with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds()[1] == 5.9, "Testing GetTimeBounds with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[0] == 0.0, "Testing GetTimeBounds(3) lower part with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[0] == 0.0, "Testing GetTimeBounds(3) lower part with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[0] == 3.9, "Testing GetTimeBounds(3) lower part with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetTimeBounds(3)[1] == 0.0, "Testing GetTimeBounds(3) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetTimeBounds(3)[1] == 0.0, "Testing GetTimeBounds(3) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetTimeBounds(3)[1] == 4.9, "Testing GetTimeBounds(3) with m_12345TimeGeometry"); } void IsValidTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(-1) == false, "Testing IsValidTimePoint(-1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(0) == false, "Testing IsValidTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(0) == true, "Testing IsValidTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(0) == false, "Testing IsValidTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(1) == false, "Testing IsValidTimePoint(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(1) == false, "Testing IsValidTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(1) == true, "Testing IsValidTimePoint(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(2.5) == false, "Testing IsValidTimePoint(2.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(2.5) == false, "Testing IsValidTimePoint(2.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(2.5) == true, "Testing IsValidTimePoint(2.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(5.89) == false, "Testing IsValidTimePoint(5.89) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(5.89) == false, "Testing IsValidTimePoint(5.89) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(5.89) == true, "Testing IsValidTimePoint(5.89) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimePoint(10) == false, "Testing IsValidTimePoint(10) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(0) == false, "Testing IsValidTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(0) == true, "Testing IsValidTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(0) == true, "Testing IsValidTimeStep(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(1) == false, "Testing IsValidTimeStep(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(1) == false, "Testing IsValidTimeStep(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(1) == true, "Testing IsValidTimeStep(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValidTimeStep(6) == false, "Testing IsValidTimeStep(6) with m_12345TimeGeometry"); + + //checked collapsed cases + MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->IsValidTimePoint(m_123TimeGeometryWithCollapsedInterim->GetMaximumTimePoint()) == false, + "Testing that m_123TimeGeometryWithCollapsedInterim does not inclued the max bound in validity"); + MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->IsValidTimePoint(m_123TimeGeometryWithCollapsedEnd->GetMaximumTimePoint()) == true, + "Testing that m_123TimeGeometryWithCollapsedEnd does inclued the max bound in validity, because it has an collapsed final time step. (see also T27259)"); + } void TimeStepToTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(0) == 0.0, "Testing TimeStepToTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(0) == 0.0, "Testing TimeStepToTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(0) == 1.0, "Testing TimeStepToTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(1) == 0.0, "Testing TimeStepToTimePoint(1) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(1) == 0.0, "Testing TimeStepToTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(1) == 2.0, "Testing TimeStepToTimePoint(1) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimeStepToTimePoint(6) == 0.0, "Testing TimeStepToTimePoint(6) with m_12345TimeGeometry"); } void TimePointToTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.0) == 0, "Testing TimePointToTimeStep(0.0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(0.5) == 0, "Testing TimePointToTimeStep(0.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(3.5) == 0, "Testing TimePointToTimeStep(3.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(3.5) == m_initTimeGeometry->CountTimeSteps(), "Testing TimePointToTimeStep(3.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(3.5) == 2, "Testing TimePointToTimeStep(3.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->TimePointToTimeStep(5.8) == 0, "Testing TimePointToTimeStep(5.8) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->TimePointToTimeStep(5.8) == m_initTimeGeometry->CountTimeSteps(), "Testing TimePointToTimeStep(5.8) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.8) == 4, "Testing TimePointToTimeStep(5.8) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->TimePointToTimeStep(5.9) == m_12345TimeGeometry->CountTimeSteps(), "Testing TimePointToTimeStep(5.9) with m_12345TimeGeometry"); + + //checked collapsed cases + MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->TimePointToTimeStep(m_123TimeGeometryWithCollapsedInterim->GetMaximumTimePoint()) == m_123TimeGeometryWithCollapsedInterim->CountTimeSteps(), + "Testing m_123TimeGeometryWithCollapsedInterim does not map the max time poit."); + MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->TimePointToTimeStep(m_123TimeGeometryWithCollapsedEnd->GetMaximumTimePoint()) == 2, + "Testing that m_123TimeGeometryWithCollapsedEnd does map the max bound, because it has an collapsed final time step. (see also T27259)"); + } void GetGeometryCloneForTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryCloneForTimeStep(0).IsNull(), "Testing GetGeometryCloneForTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(), "Testing GetGeometryCloneForTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryCloneForTimeStep(0).IsNotNull(), "Testing GetGeometryCloneForTimeStep(0) with m_12345TimeGeometry"); } void GetGeometryForTimeStep() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimeStep(0).IsNull(), "Testing GetGeometryForTimePoint(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(0).IsNotNull(), "Testing GetGeometryForTimePoint(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimeStep(1).IsNull(), "Testing GetGeometryForTimePoint(1) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(0).GetPointer() == m_Geometry1.GetPointer(), "Testing GetGeometryForTimePoint(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(3).GetPointer() == m_Geometry4.GetPointer(), "Testing GetGeometryForTimePoint(3) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(4).GetPointer() == m_Geometry5.GetPointer(), "Testing GetGeometryForTimePoint(4) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimeStep(5).IsNull(), "Testing GetGeometryForTimePoint(5) with m_12345TimeGeometry"); } void GetGeometryForTimePoint() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(0).IsNull(), "Testing GetGeometryForTimeStep(0) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(0).IsNotNull(), "Testing GetGeometryForTimeStep(0) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(0).IsNull(), "Testing GetGeometryForTimeStep(0) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(), "Testing GetGeometryForTimeStep(1.5) with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->GetGeometryForTimePoint(1.5).IsNull(), "Testing GetGeometryForTimeStep(1.5) with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimePoint(1.5).GetPointer() == m_Geometry1.GetPointer(), "Testing GetGeometryForTimeStep(1.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimePoint(3.5).GetPointer() == m_Geometry3.GetPointer(), "Testing GetGeometryForTimeStep(3.5) with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetGeometryForTimePoint(5.9).IsNull(), "Testing GetGeometryForTimeStep(5.9) with m_12345TimeGeometry"); } void IsValid() { MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->IsValid() == false, "Testing IsValid() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->IsValid() == true, "Testing IsValid() with m_initTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->IsValid() == true, "Testing IsValid() with m_12345TimeGeometry"); } void Expand() { m_12345TimeGeometry->Expand(3); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing Expand(3) doesn't change m_12345TimeGeometry"); m_12345TimeGeometry->Expand(7); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 7, "Testing Expand(7) with m_12345TimeGeometry"); } void ReplaceTimeStepGeometries() { // Test replace time step geometries m_12345TimeGeometry->ReplaceTimeStepGeometries(m_NewGeometry); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 5, "Testing ReplaceTimeStepGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(0)->GetOrigin() == m_NewGeometry->GetOrigin(), "Testing ReplaceTimeStepGeometries(): check if first geometry of m_12345TimeGeometry " "was replaced m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED( m_12345TimeGeometry->GetGeometryForTimeStep(4)->GetOrigin() == m_NewGeometry->GetOrigin(), "Testing ReplaceTimeStepGeometries(): check if last geometry of m_12345TimeGeometry " "was replaced m_12345TimeGeometry"); } void ClearAllGeometries() { // Test clear all geometries m_12345TimeGeometry->ClearAllGeometries(); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->CountTimeSteps() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMinimumTimePoint() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->GetMaximumTimePoint() == 0, "Testing ClearAllGeometries() with m_12345TimeGeometry"); } void AppendNewTimeStep() { // Test append MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(nullptr, 0, 1)); MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry3_5,m_Geometry3_5MinTP,m_Geometry3_5MaxTP)); MITK_TEST_FOR_EXCEPTION(mitk::Exception, m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MaxTP, m_Geometry4MinTP)); //valid but inverted bounds m_emptyTimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->CountTimeSteps() == 1, "Testing AppendNewTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMinimumTimePoint() == 4, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->GetMaximumTimePoint() == 4.9, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 3, "Testing AppendNewTimeStep() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 3.9, "Testing ClearAllGeometries() with m_emptyTimeGeometry"); m_123TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->CountTimeSteps() == 4, "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint() == 1, "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMaximumTimePoint() == 4.9, "Testing AppendNewTimeStep() with m_123TimeGeometry"); MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometry->GetMinimumTimePoint(3) == 3.9, "Testing AppendNewTimeStep() with m_123TimeGeometry"); } + + void HasCollapsedFinalTimeStep() + { + MITK_TEST_CONDITION_REQUIRED(m_emptyTimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_emptyTimeGeometry"); + MITK_TEST_CONDITION_REQUIRED(m_initTimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_initTimeGeometry"); + MITK_TEST_CONDITION_REQUIRED(m_12345TimeGeometry->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_12345TimeGeometry"); + MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedEnd->HasCollapsedFinalTimeStep() == true, "Testing HasCollapsedFinalTimeStep() with m_123TimeGeometryWithCollapsedEnd"); + MITK_TEST_CONDITION_REQUIRED(m_123TimeGeometryWithCollapsedInterim->HasCollapsedFinalTimeStep() == false, "Testing HasCollapsedFinalTimeStep() with m_123TimeGeometryWithCollapsedInterim"); + } + }; MITK_TEST_SUITE_REGISTRATION(mitkArbitraryTimeGeometry) diff --git a/Modules/Core/test/mitkBaseGeometryTest.cpp b/Modules/Core/test/mitkBaseGeometryTest.cpp index 79af25a94d..07b5afaf36 100644 --- a/Modules/Core/test/mitkBaseGeometryTest.cpp +++ b/Modules/Core/test/mitkBaseGeometryTest.cpp @@ -1,1343 +1,1518 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include class vtkMatrix4x4; class vtkMatrixToLinearTransform; class vtkLinearTransform; typedef itk::BoundingBox BoundingBox; typedef itk::BoundingBox BoundingBoxType; typedef BoundingBoxType::BoundsArrayType BoundsArrayType; typedef BoundingBoxType::Pointer BoundingBoxPointer; // Dummy instance of abstract base class class DummyTestClass : public mitk::BaseGeometry { public: DummyTestClass(){}; DummyTestClass(const DummyTestClass &other) : BaseGeometry(other){}; ~DummyTestClass() override{}; mitkClassMacro(DummyTestClass, mitk::BaseGeometry); itkNewMacro(Self); mitkNewMacro1Param(Self, const Self &); itk::LightObject::Pointer InternalClone() const override { Self::Pointer newGeometry = new Self(*this); newGeometry->UnRegister(); return newGeometry.GetPointer(); } protected: void PrintSelf(std::ostream & /*os*/, itk::Indent /*indent*/) const override{}; //##Documentation //## @brief Pre- and Post-functions are empty in BaseGeometry //## //## These virtual functions allow for a different beahiour in subclasses. //## Do implement them in every subclass of BaseGeometry. If not needed, use {}. //## If this class is inherited from a subclass of BaseGeometry, call {Superclass::Pre...();};, example: // SlicedGeometry3D class void PreSetSpacing(const mitk::Vector3D &/*aSpacing*/) override{}; }; class mitkBaseGeometryTestSuite : public mitk::TestFixture { // List of Tests CPPUNIT_TEST_SUITE(mitkBaseGeometryTestSuite); // Constructor MITK_TEST(TestConstructors); MITK_TEST(TestInitialize); // Set MITK_TEST(TestSetOrigin); MITK_TEST(TestSetBounds); MITK_TEST(TestSetFloatBounds); MITK_TEST(TestSetFloatBoundsDouble); MITK_TEST(TestSetFrameOfReferenceID); MITK_TEST(TestSetIndexToWorldTransform); MITK_TEST(TestSetIndexToWorldTransformWithoutChangingSpacing); MITK_TEST(TestSetIndexToWorldTransform_WithPointerToSameTransform); MITK_TEST(TestSetSpacing); MITK_TEST(TestTransferItkToVtkTransform); MITK_TEST(TestSetIndexToWorldTransformByVtkMatrix); MITK_TEST(TestSetIdentity); MITK_TEST(TestSetImageGeometry); // Equal MITK_TEST(Equal_CloneAndOriginal_ReturnsTrue); MITK_TEST(Equal_DifferentOrigin_ReturnsFalse); MITK_TEST(Equal_DifferentIndexToWorldTransform_ReturnsFalse); MITK_TEST(Equal_DifferentSpacing_ReturnsFalse); MITK_TEST(Equal_InputIsNull_ReturnsFalse); MITK_TEST(Equal_DifferentBoundingBox_ReturnsFalse); MITK_TEST(Equal_Transforms_MinorDifferences_And_Eps); // other Functions MITK_TEST(TestComposeTransform); MITK_TEST(TestComposeVtkMatrix); MITK_TEST(TestTranslate); MITK_TEST(TestIndexToWorld); MITK_TEST(TestExecuteOperation); MITK_TEST(TestCalculateBoundingBoxRelToTransform); // MITK_TEST(TestSetTimeBounds); MITK_TEST(TestIs2DConvertable); MITK_TEST(TestGetCornerPoint); MITK_TEST(TestExtentInMM); MITK_TEST(TestGetAxisVector); MITK_TEST(TestGetCenter); MITK_TEST(TestGetDiagonalLength); MITK_TEST(TestGetExtent); MITK_TEST(TestIsInside); MITK_TEST(TestGetMatrixColumn); + // test IsSubGeometry + MITK_TEST(IsSubGeometry_Spacing); + MITK_TEST(IsSubGeometry_TransformMatrix); + MITK_TEST(IsSubGeometry_Bounds); + MITK_TEST(IsSubGeometry_Grid); CPPUNIT_TEST_SUITE_END(); // Used Variables private: mitk::Point3D aPoint; float aFloatSpacing[3]; mitk::Vector3D aSpacing; mitk::AffineTransform3D::Pointer aTransform; BoundingBoxPointer aBoundingBox; mitk::AffineTransform3D::MatrixType aMatrix; mitk::Point3D anotherPoint; mitk::Vector3D anotherSpacing; BoundingBoxPointer anotherBoundingBox; BoundingBoxPointer aThirdBoundingBox; mitk::AffineTransform3D::Pointer anotherTransform; mitk::AffineTransform3D::Pointer aThirdTransform; mitk::AffineTransform3D::MatrixType anotherMatrix; mitk::AffineTransform3D::MatrixType aThirdMatrix; DummyTestClass::Pointer aDummyGeometry; DummyTestClass::Pointer anotherDummyGeometry; public: // Set up for variables void setUp() override { mitk::FillVector3D(aFloatSpacing, 1, 1, 1); mitk::FillVector3D(aSpacing, 1, 1, 1); mitk::FillVector3D(aPoint, 0, 0, 0); // Transform aTransform = mitk::AffineTransform3D::New(); aTransform->SetIdentity(); aMatrix.SetIdentity(); anotherTransform = mitk::AffineTransform3D::New(); anotherMatrix.SetIdentity(); anotherMatrix(1, 1) = 2; anotherTransform->SetMatrix(anotherMatrix); aThirdTransform = mitk::AffineTransform3D::New(); aThirdMatrix.SetIdentity(); aThirdMatrix(1, 1) = 7; aThirdTransform->SetMatrix(aThirdMatrix); // Bounding Box float bounds[6] = {0, 1, 0, 1, 0, 1}; mitk::BoundingBox::BoundsArrayType b; const float *input = bounds; int j = 0; for (mitk::BoundingBox::BoundsArrayType::Iterator it = b.Begin(); j < 6; ++j) *it++ = (mitk::ScalarType)*input++; aBoundingBox = BoundingBoxType::New(); BoundingBoxType::PointsContainer::Pointer pointscontainer = BoundingBoxType::PointsContainer::New(); BoundingBoxType::PointType p; BoundingBoxType::PointIdentifier pointid; for (pointid = 0; pointid < 2; ++pointid) { unsigned int i; for (i = 0; i < 3; ++i) { p[i] = bounds[2 * i + pointid]; } pointscontainer->InsertElement(pointid, p); } aBoundingBox->SetPoints(pointscontainer); aBoundingBox->ComputeBoundingBox(); anotherBoundingBox = BoundingBoxType::New(); p[0] = 11; p[1] = 12; p[2] = 13; pointscontainer->InsertElement(1, p); anotherBoundingBox->SetPoints(pointscontainer); anotherBoundingBox->ComputeBoundingBox(); aThirdBoundingBox = BoundingBoxType::New(); p[0] = 22; p[1] = 23; p[2] = 24; pointscontainer->InsertElement(1, p); aThirdBoundingBox->SetPoints(pointscontainer); aThirdBoundingBox->ComputeBoundingBox(); mitk::FillVector3D(anotherPoint, 2, 3, 4); mitk::FillVector3D(anotherSpacing, 5, 6.5, 7); aDummyGeometry = DummyTestClass::New(); aDummyGeometry->Initialize(); anotherDummyGeometry = aDummyGeometry->Clone(); } void tearDown() override { aDummyGeometry = nullptr; anotherDummyGeometry = nullptr; } // Test functions void TestSetOrigin() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // undo changes, new and changed object need to be the same! dummy->SetOrigin(aPoint); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetOrigin"); } void TestSetImageGeometry() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetImageGeometry(true); CPPUNIT_ASSERT(dummy->GetImageGeometry()); // undo changes, new and changed object need to be the same! dummy->SetImageGeometry(false); CPPUNIT_ASSERT(dummy->GetImageGeometry() == false); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "TestSetImageGeometry"); } void TestSetFloatBounds() { float bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox equality"); // Wrong bounds, test needs to fail bounds[1] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "BoundingBox not equal"); // undo changes, new and changed object need to be the same! float originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo and equal"); } void TestSetBounds() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetBounds(anotherBoundingBox->GetBounds()); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting bounds"); // Test needs to fail now dummy->SetBounds(aThirdBoundingBox->GetBounds()); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Setting unequal bounds"); // undo changes, new and changed object need to be the same! dummy->SetBounds(aBoundingBox->GetBounds()); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set bounds"); } void TestSetFloatBoundsDouble() { double bounds[6] = {0, 11, 0, 12, 0, 13}; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds"); // Test needs to fail now bounds[3] = 7; dummy->SetFloatBounds(bounds); MITK_ASSERT_NOT_EQUAL( BoundingBox::ConstPointer(dummy->GetBoundingBox()), anotherBoundingBox, "Float bounds unequal"); // undo changes, new and changed object need to be the same! double originalBounds[6] = {0, 1, 0, 1, 0, 1}; dummy->SetFloatBounds(originalBounds); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set float bounds"); } void TestSetFrameOfReferenceID() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetFrameOfReferenceID(5); CPPUNIT_ASSERT(dummy->GetFrameOfReferenceID() == 5); // undo changes, new and changed object need to be the same! dummy->SetFrameOfReferenceID(0); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Undo set frame of reference"); } void TestSetIndexToWorldTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 1"); // Test needs to fail now dummy->SetIndexToWorldTransform(aThirdTransform); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransform 2"); // undo changes, new and changed object need to be the same! dummy->SetIndexToWorldTransform(aTransform); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransform 3"); } void TestSetIndexToWorldTransformWithoutChangingSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformWithoutChangingSpacing(anotherTransform); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing(), mitk::eps, true)); // calculate a new version of anotherTransform, so that the spacing should be the same as the original spacing of // aTransform. mitk::AffineTransform3D::MatrixType::InternalMatrixType vnlmatrix; vnlmatrix = anotherTransform->GetMatrix().GetVnlMatrix(); mitk::VnlVector col; col = vnlmatrix.get_column(0); col.normalize(); col *= aSpacing[0]; vnlmatrix.set_column(0, col); col = vnlmatrix.get_column(1); col.normalize(); col *= aSpacing[1]; vnlmatrix.set_column(1, col); col = vnlmatrix.get_column(2); col.normalize(); col *= aSpacing[2]; vnlmatrix.set_column(2, col); mitk::Matrix3D matrix; matrix = vnlmatrix; anotherTransform->SetMatrix(matrix); CPPUNIT_ASSERT(mitk::Equal(anotherTransform, dummy->GetIndexToWorldTransform(), mitk::eps, true)); } void TestSetIndexToWorldTransform_WithPointerToSameTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); mitk::AffineTransform3D::Pointer testTransfrom = dummy->GetIndexToWorldTransform(); mitk::Vector3D modifiedPoint = anotherPoint.GetVectorFromOrigin() * 2.; testTransfrom->SetOffset(modifiedPoint); dummy->SetIndexToWorldTransform(testTransfrom); CPPUNIT_ASSERT(mitk::Equal(modifiedPoint, dummy->GetOrigin().GetVectorFromOrigin())); } void TestSetIndexToWorldTransformByVtkMatrix() { vtkMatrix4x4 *vtkmatrix; vtkmatrix = vtkMatrix4x4::New(); vtkmatrix->Identity(); vtkmatrix->SetElement(1, 1, 2); DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 1"); // test needs to fail now vtkmatrix->SetElement(1, 1, 7); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); MITK_ASSERT_NOT_EQUAL(anotherTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compare IndexToWorldTransformByVtkMatrix 2"); // undo changes, new and changed object need to be the same! vtkmatrix->SetElement(1, 1, 1); dummy->SetIndexToWorldTransformByVtkMatrix(vtkmatrix); vtkmatrix->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compare IndexToWorldTransformByVtkMatrix 3"); } void TestSetIdentity() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Change IndextoWorldTransform and Origin dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetOrigin(anotherPoint); // Set Identity should reset ITWT and Origin dummy->SetIdentity(); MITK_ASSERT_EQUAL( aTransform, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Test set identity 1"); CPPUNIT_ASSERT(mitk::Equal(aPoint, dummy->GetOrigin())); CPPUNIT_ASSERT(mitk::Equal(aSpacing, dummy->GetSpacing())); // new and changed object need to be the same! DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Test set identity 2"); } void TestSetSpacing() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); // undo changes, new and changed object need to be the same! dummy->SetSpacing(aSpacing); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy spacing"); } void TestTransferItkToVtkTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); // calls TransferItkToVtkTransform mitk::AffineTransform3D::Pointer dummyTransform = dummy->GetIndexToWorldTransform(); CPPUNIT_ASSERT(mitk::MatrixEqualElementWise(anotherMatrix, dummyTransform->GetMatrix())); } void TestConstructors() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); bool test = dummy1->IsValid(); CPPUNIT_ASSERT(test == true); CPPUNIT_ASSERT(dummy1->GetFrameOfReferenceID() == 0); CPPUNIT_ASSERT(dummy1->GetIndexToWorldTransformLastModified() == 0); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetSpacing(), aSpacing)); CPPUNIT_ASSERT(mitk::Equal(dummy1->GetOrigin(), aPoint)); CPPUNIT_ASSERT(dummy1->GetImageGeometry() == false); MITK_ASSERT_EQUAL( mitk::AffineTransform3D::Pointer(dummy1->GetIndexToWorldTransform()), aTransform, "Contructor test 1"); MITK_ASSERT_EQUAL( mitk::BaseGeometry::BoundingBoxType::ConstPointer(dummy1->GetBoundingBox()), aBoundingBox, "Constructor test 2"); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); float bounds[6] = {0, 11, 0, 12, 0, 13}; dummy2->SetFloatBounds(bounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); DummyTestClass::Pointer dummy3 = DummyTestClass::New(*dummy2); MITK_ASSERT_EQUAL(dummy3, dummy2, "Dummy contructor"); } // Equal Tests void Equal_CloneAndOriginal_ReturnsTrue() { MITK_ASSERT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Clone test"); } void Equal_DifferentOrigin_ReturnsFalse() { anotherDummyGeometry->SetOrigin(anotherPoint); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different origin test"); } void Equal_DifferentIndexToWorldTransform_ReturnsFalse() { anotherDummyGeometry->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different index to world"); } void Equal_DifferentSpacing_ReturnsFalse() { anotherDummyGeometry->SetSpacing(anotherSpacing); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different spacing"); } void Equal_InputIsNull_ReturnsFalse() { DummyTestClass::Pointer geometryNull = nullptr; CPPUNIT_ASSERT_THROW(MITK_ASSERT_EQUAL(geometryNull, anotherDummyGeometry, "Input is null"), mitk::Exception); } void Equal_DifferentBoundingBox_ReturnsFalse() { // create different bounds to make the comparison false mitk::ScalarType bounds[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; anotherDummyGeometry->SetBounds(bounds); MITK_ASSERT_NOT_EQUAL(aDummyGeometry, anotherDummyGeometry, "Different bounding box"); } void Equal_Transforms_MinorDifferences_And_Eps() { // Verifies that the eps parameter is evaluated properly // when comparing two mitk::BaseGeometry::TransformTypes aMatrix.SetIdentity(); anotherMatrix.SetIdentity(); aMatrix(0, 1) = 0.0002; aTransform->SetMatrix(aMatrix); anotherMatrix(0, 1) = 0.0002; anotherTransform->SetMatrix(anotherMatrix); anotherTransform->SetMatrix(aMatrix); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=mitk::eps", mitk::Equal(aTransform, anotherTransform, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE("Exact same transforms are mitk::Equal() for eps=vnl_math::eps", mitk::Equal(aTransform, anotherTransform, vnl_math::eps, true)); anotherMatrix(0, 1) = 0.0002 + mitk::eps; anotherTransform->SetMatrix(anotherMatrix); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=vnl_math::eps", !mitk::Equal(aTransform, anotherTransform, vnl_math::eps, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps are !mitk::Equal() for eps=mitk::eps-1%", !mitk::Equal(aTransform, anotherTransform, mitk::eps * 0.99, true)); CPPUNIT_ASSERT_MESSAGE("Transforms of diff mitk::eps _are_ mitk::Equal() for eps=mitk::eps+1%", mitk::Equal(aTransform, anotherTransform, mitk::eps * 1.01, true)); } void TestComposeTransform() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 mitk::AffineTransform3D::Pointer transform2; transform2 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix2; matrix2.SetIdentity(); matrix2(1, 1) = 2; transform2->SetMatrix(matrix2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 mitk::AffineTransform3D::Pointer transform4; transform4 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix4; matrix4.SetIdentity(); matrix4(1, 1) = 0.25; transform4->SetMatrix(matrix4); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(transform2); // Spacing = 4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose transform 2"); // 4=4 // undo changes, new and changed object need to be the same! dummy->Compose(transform4); // Spacing = 1 DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose transform 3"); // 1=1 } void TestComposeVtkMatrix() { // Create Transformations to set and compare mitk::AffineTransform3D::Pointer transform1; transform1 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix1; matrix1.SetIdentity(); matrix1(1, 1) = 2; transform1->SetMatrix(matrix1); // Spacing = 2 vtkMatrix4x4 *vtkmatrix2; vtkmatrix2 = vtkMatrix4x4::New(); vtkmatrix2->Identity(); vtkmatrix2->SetElement(1, 1, 2); // Spacing = 2 mitk::AffineTransform3D::Pointer transform3; transform3 = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType matrix3; matrix3.SetIdentity(); matrix3(1, 1) = 4; transform3->SetMatrix(matrix3); // Spacing = 4 vtkMatrix4x4 *vtkmatrix4; vtkmatrix4 = vtkMatrix4x4::New(); vtkmatrix4->Identity(); vtkmatrix4->SetElement(1, 1, 0.25); // Spacing = 0.25 // Vector to compare spacing mitk::Vector3D expectedSpacing; expectedSpacing.Fill(1.0); expectedSpacing[1] = 4; DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(transform1); // Spacing = 2 dummy->Compose(vtkmatrix2); // Spacing = 4 vtkmatrix2->Delete(); MITK_ASSERT_EQUAL( transform3, mitk::AffineTransform3D::Pointer(dummy->GetIndexToWorldTransform()), "Compose vtk matrix"); // 4=4 CPPUNIT_ASSERT(mitk::Equal(dummy->GetSpacing(), expectedSpacing)); // undo changes, new and changed object need to be the same! dummy->Compose(vtkmatrix4); // Spacing = 1 vtkmatrix4->Delete(); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Compose vtk"); // 1=1 } void TestTranslate() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); // use some random values for translation mitk::Vector3D translationVector; translationVector.SetElement(0, 17.5f); translationVector.SetElement(1, -32.3f); translationVector.SetElement(2, 4.0f); // compute ground truth mitk::Point3D tmpResult = anotherPoint + translationVector; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), tmpResult)); // undo changes translationVector *= -1; dummy->Translate(translationVector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetOrigin(), anotherPoint)); // undo changes, new and changed object need to be the same! translationVector.SetElement(0, -1 * anotherPoint[0]); translationVector.SetElement(1, -1 * anotherPoint[1]); translationVector.SetElement(2, -1 * anotherPoint[2]); dummy->Translate(translationVector); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Translate test"); } // a part of the test requires axis-parallel coordinates int testIndexAndWorldConsistency(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems mitk::Point3D origin = dummyGeometry->GetOrigin(); mitk::Point3D dummyPoint; // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(origin, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, origin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, mitk::Point3D)==(0,0,0) mitk::Point3D globalOrigin; mitk::FillVector3D(globalOrigin, 0, 0, 0); mitk::Point3D originContinuousIndex; dummyGeometry->WorldToIndex(origin, originContinuousIndex); CPPUNIT_ASSERT(mitk::EqualArray(originContinuousIndex, globalOrigin, 3, mitk::eps, true)); // Testing WorldToIndex(origin, itk::Index)==(0,0,0) itk::Index<3> itkindex; dummyGeometry->WorldToIndex(origin, itkindex); itk::Index<3> globalOriginIndex; mitk::vtk2itk(globalOrigin, globalOriginIndex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin-0.5*spacing, itk::Index)==(0,0,0) mitk::Vector3D halfSpacingStep = dummyGeometry->GetSpacing() * 0.5; mitk::Matrix3D rotation; mitk::Point3D originOffCenter = origin - halfSpacingStep; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing-eps, itk::Index)==(0,0,0) originOffCenter = origin + halfSpacingStep; originOffCenter -= 0.0001; dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, globalOriginIndex, 3, mitk::eps, true)); // Testing WorldToIndex(origin+0.5*spacing, itk::Index)==(1,1,1)"); originOffCenter = origin + halfSpacingStep; itk::Index<3> global111; mitk::FillVector3D(global111, 1, 1, 1); dummyGeometry->WorldToIndex(originOffCenter, itkindex); CPPUNIT_ASSERT(mitk::EqualArray(itkindex, global111, 3, mitk::eps, true)); // Testing WorldToIndex(GetCenter())==BoundingBox.GetCenter mitk::Point3D center = dummyGeometry->GetCenter(); mitk::Point3D centerContIndex; dummyGeometry->WorldToIndex(center, centerContIndex); mitk::BoundingBox::ConstPointer boundingBox = dummyGeometry->GetBoundingBox(); mitk::BoundingBox::PointType centerBounds = boundingBox->GetCenter(); CPPUNIT_ASSERT(mitk::Equal(centerContIndex, centerBounds)); // Testing GetCenter()==IndexToWorld(BoundingBox.GetCenter) center = dummyGeometry->GetCenter(); mitk::Point3D centerBoundsInWorldCoords; dummyGeometry->IndexToWorld(centerBounds, centerBoundsInWorldCoords); CPPUNIT_ASSERT(mitk::Equal(center, centerBoundsInWorldCoords)); // Test using random point, // Testing consistency of index and world coordinate systems mitk::Point3D point; mitk::FillVector3D(point, 3.5, -2, 4.6); // Testing index->world->index conversion consistency dummyGeometry->WorldToIndex(point, dummyPoint); dummyGeometry->IndexToWorld(dummyPoint, dummyPoint); CPPUNIT_ASSERT(mitk::EqualArray(dummyPoint, point, 3, mitk::eps, true)); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForVectors(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems for vectors mitk::Vector3D xAxisMM = dummyGeometry->GetAxisVector(0); mitk::Vector3D xAxisContinuousIndex; mitk::Point3D p, pIndex, origin; origin = dummyGeometry->GetOrigin(); p[0] = xAxisMM[0] + origin[0]; p[1] = xAxisMM[1] + origin[1]; p[2] = xAxisMM[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(xAxisMM, xAxisContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(xAxisContinuousIndex, xAxisContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(xAxisContinuousIndex == xAxisMM); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(xAxisContinuousIndex[2], p[2] - origin[2])); // Test consictency for random vector mitk::Vector3D vector; mitk::FillVector3D(vector, 2.5, -3.2, 8.1); mitk::Vector3D vectorContinuousIndex; p[0] = vector[0] + origin[0]; p[1] = vector[1] + origin[1]; p[2] = vector[2] + origin[2]; dummyGeometry->WorldToIndex(p, pIndex); dummyGeometry->WorldToIndex(vector, vectorContinuousIndex); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], pIndex[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], pIndex[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], pIndex[2])); dummyGeometry->IndexToWorld(vectorContinuousIndex, vectorContinuousIndex); dummyGeometry->IndexToWorld(pIndex, p); CPPUNIT_ASSERT(vectorContinuousIndex == vector); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[0], p[0] - origin[0])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[1], p[1] - origin[1])); CPPUNIT_ASSERT(mitk::Equal(vectorContinuousIndex[2], p[2] - origin[2])); return EXIT_SUCCESS; } int testIndexAndWorldConsistencyForIndex(DummyTestClass::Pointer dummyGeometry) { // Testing consistency of index and world coordinate systems // creating testing data itk::Index<4> itkIndex4, itkIndex4b; itk::Index<3> itkIndex3, itkIndex3b; itk::Index<2> itkIndex2, itkIndex2b; itk::Index<3> mitkIndex, mitkIndexb; itkIndex4[0] = itkIndex4[1] = itkIndex4[2] = itkIndex4[3] = 4; itkIndex3[0] = itkIndex3[1] = itkIndex3[2] = 6; itkIndex2[0] = itkIndex2[1] = 2; mitkIndex[0] = mitkIndex[1] = mitkIndex[2] = 13; // check for constistency mitk::Point3D point; dummyGeometry->IndexToWorld(itkIndex2, point); dummyGeometry->WorldToIndex(point, itkIndex2b); CPPUNIT_ASSERT(((itkIndex2b[0] == itkIndex2[0]) && (itkIndex2b[1] == itkIndex2[1]))); // Testing itk::index<2> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex3, point); dummyGeometry->WorldToIndex(point, itkIndex3b); CPPUNIT_ASSERT( ((itkIndex3b[0] == itkIndex3[0]) && (itkIndex3b[1] == itkIndex3[1]) && (itkIndex3b[2] == itkIndex3[2]))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(itkIndex4, point); dummyGeometry->WorldToIndex(point, itkIndex4b); CPPUNIT_ASSERT(((itkIndex4b[0] == itkIndex4[0]) && (itkIndex4b[1] == itkIndex4[1]) && (itkIndex4b[2] == itkIndex4[2]) && (itkIndex4b[3] == 0))); // Testing itk::index<3> for IndexToWorld/WorldToIndex consistency dummyGeometry->IndexToWorld(mitkIndex, point); dummyGeometry->WorldToIndex(point, mitkIndexb); CPPUNIT_ASSERT( ((mitkIndexb[0] == mitkIndex[0]) && (mitkIndexb[1] == mitkIndex[1]) && (mitkIndexb[2] == mitkIndex[2]))); // Testing mitk::Index for IndexToWorld/WorldToIndex consistency return EXIT_SUCCESS; } void TestIndexToWorld() { DummyTestClass::Pointer dummy = DummyTestClass::New(); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); // Geometry must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy index to world"); // Test with other geometries dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetIndexToWorldTransform(anotherTransform); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetOrigin(anotherPoint); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); dummy->SetSpacing(anotherSpacing); testIndexAndWorldConsistency(dummy); testIndexAndWorldConsistencyForVectors(dummy); testIndexAndWorldConsistencyForIndex(dummy); } void TestExecuteOperation() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // Do same Operations with new Dummy and compare DummyTestClass::Pointer newDummy = DummyTestClass::New(); // Test operation Nothing auto opN = new mitk::Operation(mitk::OpNOTHING); dummy->ExecuteOperation(opN); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 1"); // Test operation Move auto opP = new mitk::PointOperation(mitk::OpMOVE, anotherPoint); dummy->ExecuteOperation(opP); CPPUNIT_ASSERT(mitk::Equal(anotherPoint, dummy->GetOrigin())); newDummy->SetOrigin(anotherPoint); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 2"); // Test operation Scale, Scale sets spacing to scale+1 mitk::Point3D spacing; spacing[0] = anotherSpacing[0] - 1.; spacing[1] = anotherSpacing[1] - 1.; spacing[2] = anotherSpacing[2] - 1.; auto opS = new mitk::ScaleOperation(mitk::OpSCALE, spacing, anotherPoint); dummy->ExecuteOperation(opS); CPPUNIT_ASSERT(mitk::Equal(anotherSpacing, dummy->GetSpacing())); newDummy->SetSpacing(anotherSpacing); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy execute operation 3"); // change Geometry to test more cases dummy->SetIndexToWorldTransform(anotherTransform); dummy->SetSpacing(anotherSpacing); // Testing a rotation of the geometry double angle = 35.0; mitk::Vector3D rotationVector; mitk::FillVector3D(rotationVector, 1, 0, 0); mitk::Point3D center = dummy->GetCenter(); auto opR = new mitk::RotationOperation(mitk::OpROTATE, center, rotationVector, angle); dummy->ExecuteOperation(opR); mitk::Matrix3D rotation; mitk::GetRotation(dummy, rotation); mitk::Vector3D voxelStep = rotation * anotherSpacing; mitk::Vector3D voxelStepIndex; dummy->WorldToIndex(voxelStep, voxelStepIndex); mitk::Vector3D expectedVoxelStepIndex; expectedVoxelStepIndex.Fill(1); CPPUNIT_ASSERT(mitk::Equal(voxelStepIndex, expectedVoxelStepIndex)); delete opR; delete opN; delete opS; delete opP; } void TestCalculateBoundingBoxRelToTransform() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 15); dummy->SetExtentInMM(1, 20); dummy->SetExtentInMM(2, 8); mitk::BoundingBox::Pointer dummyBoundingBox = dummy->CalculateBoundingBoxRelativeToTransform(anotherTransform); mitk::BoundingBox::PointsContainer::Pointer pointscontainer = mitk::BoundingBox::PointsContainer::New(); mitk::BoundingBox::PointIdentifier pointid = 0; unsigned char i; mitk::AffineTransform3D::Pointer inverse = mitk::AffineTransform3D::New(); anotherTransform->GetInverse(inverse); for (i = 0; i < 8; ++i) pointscontainer->InsertElement(pointid++, inverse->TransformPoint(dummy->GetCornerPoint(i))); mitk::BoundingBox::Pointer result = mitk::BoundingBox::New(); result->SetPoints(pointscontainer); result->ComputeBoundingBox(); MITK_ASSERT_EQUAL(result, dummyBoundingBox, "BBox rel to transform"); // dummy still needs to be unchanged, except for extend DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetExtentInMM(0, 15); newDummy->SetExtentInMM(1, 20); newDummy->SetExtentInMM(2, 8); MITK_ASSERT_EQUAL(dummy, newDummy, "Dummy BBox"); } // void TestSetTimeBounds(){ // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // DummyTestClass::Pointer dummy = DummyTestClass::New(); // dummy->SetTimeBounds(timeBounds); // mitk::TimeBounds timeBounds2 = dummy->GetTimeBounds(); // CPPUNIT_ASSERT(timeBounds[0]==timeBounds2[0]); // CPPUNIT_ASSERT(timeBounds[1]==timeBounds2[1]); // //undo changes, new and changed object need to be the same! // timeBounds[0]=mitk::ScalarTypeNumericTraits::NonpositiveMin(); // timeBounds[1]=mitk::ScalarTypeNumericTraits::max(); // DummyTestClass::Pointer newDummy = DummyTestClass::New(); // CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); //} void TestIs2DConvertable() { DummyTestClass::Pointer dummy = DummyTestClass::New(); // new initialized geometry is 2D convertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Spacing needs to fail dummy->SetSpacing(anotherSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetSpacing(aSpacing); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // Wrong Origin needs to fail dummy->SetOrigin(anotherPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo dummy->SetOrigin(aPoint); CPPUNIT_ASSERT(dummy->Is2DConvertable()); // third dimension must not be transformed mitk::AffineTransform3D::Pointer dummyTransform = mitk::AffineTransform3D::New(); mitk::AffineTransform3D::MatrixType dummyMatrix; dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); dummy->SetIndexToWorldTransform(dummyTransform); // identity matrix is 2DConvertable CPPUNIT_ASSERT(dummy->Is2DConvertable()); dummyMatrix(0, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(1, 2) = 0.4; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 2) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 1) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); dummyMatrix.SetIdentity(); dummyMatrix(2, 0) = 3; dummyTransform->SetMatrix(dummyMatrix); CPPUNIT_ASSERT(dummy->Is2DConvertable() == false); // undo changes, new and changed object need to be the same! dummyMatrix.SetIdentity(); dummyTransform->SetMatrix(dummyMatrix); DummyTestClass::Pointer newDummy = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy, newDummy, "Is 2D convertable"); } void TestGetCornerPoint() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D corner, refCorner; // Corner 0 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(0); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 1 mitk::FillVector3D(refCorner, bounds[0], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(1); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 2 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(2); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 3 mitk::FillVector3D(refCorner, bounds[0], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(3); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(true, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 4 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(4); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 5 mitk::FillVector3D(refCorner, bounds[1], bounds[2], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(5); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, true, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 6 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[4]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(6); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, true); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Corner 7 mitk::FillVector3D(refCorner, bounds[1], bounds[3], bounds[5]); refCorner = anotherTransform->TransformPoint(refCorner); corner = dummy->GetCornerPoint(7); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); corner = dummy->GetCornerPoint(false, false, false); CPPUNIT_ASSERT(mitk::Equal(refCorner, corner)); // Wrong Corner needs to fail CPPUNIT_ASSERT_THROW(dummy->GetCornerPoint(20), itk::ExceptionObject); // dummy geometry must not have changed! DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Corner point"); } void TestExtentInMM() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetExtentInMM(0, 50); CPPUNIT_ASSERT(mitk::Equal(50., dummy->GetExtentInMM(0))); // Vnl Matrix has changed. The next line only works because the spacing is 1! CPPUNIT_ASSERT( mitk::Equal(50., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); // Smaller extent than original dummy->SetExtentInMM(0, 5); CPPUNIT_ASSERT(mitk::Equal(5., dummy->GetExtentInMM(0))); CPPUNIT_ASSERT( mitk::Equal(5., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(0).magnitude())); dummy->SetExtentInMM(1, 4); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtentInMM(1))); CPPUNIT_ASSERT( mitk::Equal(4., dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(1).magnitude())); dummy->SetExtentInMM(2, 2.5); CPPUNIT_ASSERT(mitk::Equal(2.5, dummy->GetExtentInMM(2))); CPPUNIT_ASSERT( mitk::Equal(2.5, dummy->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(2).magnitude())); } void TestGetAxisVector() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 0, 12, 0, 13}; dummy->SetFloatBounds(bounds); mitk::Vector3D vector; mitk::FillVector3D(vector, bounds[1], 0, 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(0), vector)); mitk::FillVector3D(vector, 0, bounds[3], 0); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(1), vector)); mitk::FillVector3D(vector, 0, 0, bounds[5]); dummy->IndexToWorld(vector, vector); CPPUNIT_ASSERT(mitk::Equal(dummy->GetAxisVector(2), vector)); } void TestGetCenter() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); double bounds[6] = {0, 11, 2, 12, 1, 13}; dummy->SetFloatBounds(bounds); mitk::Point3D refCenter; for (int i = 0; i < 3; i++) refCenter.SetElement(i, (bounds[2 * i] + bounds[2 * i + 1]) / 2.0); dummy->IndexToWorld(refCenter, refCenter); CPPUNIT_ASSERT(mitk::Equal(dummy->GetCenter(), refCenter)); } void TestGetDiagonalLength() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); // 3-1=2, 8-5=3, 11.5-7.5=4; 2^2+3^2+4^2 = 29 double expectedLength = sqrt(29.); CPPUNIT_ASSERT(mitk::Equal(expectedLength, dummy->GetDiagonalLength(), mitk::eps, true)); CPPUNIT_ASSERT(mitk::Equal(29., dummy->GetDiagonalLength2(), mitk::eps, true)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Diagonal length"); } void TestGetExtent() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); CPPUNIT_ASSERT(mitk::Equal(2., dummy->GetExtent(0))); CPPUNIT_ASSERT(mitk::Equal(3., dummy->GetExtent(1))); CPPUNIT_ASSERT(mitk::Equal(4., dummy->GetExtent(2))); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Extend"); } void TestIsInside() { DummyTestClass::Pointer dummy = DummyTestClass::New(); double bounds[6] = {1, 3, 5, 8, 7.5, 11.5}; dummy->SetFloatBounds(bounds); mitk::Point3D insidePoint; mitk::Point3D outsidePoint; mitk::FillVector3D(insidePoint, 2, 6, 7.6); mitk::FillVector3D(outsidePoint, 0, 9, 8.2); CPPUNIT_ASSERT(dummy->IsIndexInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsIndexInside(outsidePoint)); dummy->IndexToWorld(insidePoint, insidePoint); dummy->IndexToWorld(outsidePoint, outsidePoint); CPPUNIT_ASSERT(dummy->IsInside(insidePoint)); CPPUNIT_ASSERT(false == dummy->IsInside(outsidePoint)); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetFloatBounds(bounds); MITK_ASSERT_EQUAL(dummy, newDummy, "Is inside"); } void TestInitialize() { // test standard constructor DummyTestClass::Pointer dummy1 = DummyTestClass::New(); DummyTestClass::Pointer dummy2 = DummyTestClass::New(); dummy2->SetOrigin(anotherPoint); dummy2->SetBounds(anotherBoundingBox->GetBounds()); // mitk::TimeBounds timeBounds; // timeBounds[0] = 1; // timeBounds[1] = 9; // dummy2->SetTimeBounds(timeBounds); dummy2->SetIndexToWorldTransform(anotherTransform); dummy2->SetSpacing(anotherSpacing); dummy1->InitializeGeometry(dummy2); MITK_ASSERT_EQUAL(dummy1, dummy2, "Initialize 1"); dummy1->Initialize(); DummyTestClass::Pointer dummy3 = DummyTestClass::New(); MITK_ASSERT_EQUAL(dummy3, dummy1, "Initialize 2"); } void TestGetMatrixColumn() { DummyTestClass::Pointer dummy = DummyTestClass::New(); dummy->SetIndexToWorldTransform(anotherTransform); mitk::Vector3D testVector, refVector; testVector.SetVnlVector(dummy->GetMatrixColumn(0)); mitk::FillVector3D(refVector, 1, 0, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(1)); mitk::FillVector3D(refVector, 0, 2, 0); CPPUNIT_ASSERT(testVector == refVector); testVector.SetVnlVector(dummy->GetMatrixColumn(2)); mitk::FillVector3D(refVector, 0, 0, 1); CPPUNIT_ASSERT(testVector == refVector); // dummy must not have changed DummyTestClass::Pointer newDummy = DummyTestClass::New(); newDummy->SetIndexToWorldTransform(anotherTransform); MITK_ASSERT_EQUAL(dummy, newDummy, "GetMatrixColumn"); } + void IsSubGeometry_Spacing() + { + CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); + + for (unsigned int i = 0; i < 3; ++i) + { + mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); + wrongSpacing[i] += mitk::eps * 2; + auto wrongGeometry = aDummyGeometry->Clone(); + wrongGeometry->SetSpacing(wrongSpacing); + + CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); + } + for (unsigned int i = 0; i < 3; ++i) + { + mitk::Vector3D wrongSpacing = aDummyGeometry->GetSpacing(); + wrongSpacing[i] -= mitk::eps * 2; + auto wrongGeometry = aDummyGeometry->Clone(); + wrongGeometry->SetSpacing(wrongSpacing); + + CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); + } + } + + void IsSubGeometry_TransformMatrix() + { + CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); + + for (unsigned int i = 0; i < 3; ++i) + { + for (unsigned int j = 0; j < 3; ++j) + { + itk::Matrix wrongMatrix = aDummyGeometry->GetIndexToWorldTransform()->GetMatrix(); + wrongMatrix[i][j] += mitk::eps * 2; + auto wrongGeometry = aDummyGeometry->Clone(); + wrongGeometry->GetIndexToWorldTransform()->SetMatrix(wrongMatrix); + + CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); + } + } + } + + void IsSubGeometry_Bounds() + { + IsSubGeometry_Bounds_internal(false); + IsSubGeometry_Bounds_internal(true); + } + + void IsSubGeometry_Bounds_internal(bool isImage) + { + auto newBounds = aDummyGeometry->GetBounds(); + newBounds[0] = 10; + newBounds[1] = 20; + newBounds[2] = 10; + newBounds[3] = 20; + newBounds[4] = 10; + newBounds[5] = 20; + aDummyGeometry->SetBounds(newBounds); + aDummyGeometry->SetImageGeometry(isImage); + + CPPUNIT_ASSERT(mitk::IsSubGeometry(*aDummyGeometry, *aDummyGeometry, mitk::eps, true)); + + for (unsigned int i = 0; i < 6; ++i) + { + auto legalBounds = newBounds; + if (i % 2 == 0) + { + legalBounds[i] += 1; + } + else + { + legalBounds[i] -= 1; + } + auto legalGeometry = aDummyGeometry->Clone(); + legalGeometry->SetBounds(legalBounds); + + CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); + } + + for (unsigned int i = 0; i < 6; ++i) + { + auto wrongBounds = aDummyGeometry->GetBounds(); + if (i % 2 == 0) + { + wrongBounds[i] -= 1; + } + else + { + wrongBounds[i] += 1; + } + auto wrongGeometry = aDummyGeometry->Clone(); + wrongGeometry->SetBounds(wrongBounds); + + CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); + } + } + + void IsSubGeometry_Grid() + { + IsSubGeometry_Grid_internal(true); + IsSubGeometry_Grid_internal(false); + } + + void IsSubGeometry_Grid_internal(bool isImage) + { + auto newBounds = aDummyGeometry->GetBounds(); + newBounds[0] = 0; + newBounds[1] = 20; + newBounds[2] = 0; + newBounds[3] = 20; + newBounds[4] = 0; + newBounds[5] = 20; + aDummyGeometry->SetBounds(newBounds); + aDummyGeometry->SetImageGeometry(isImage); + + auto smallerGeometry = aDummyGeometry->Clone(); + newBounds[0] = 5; + newBounds[1] = 10; + newBounds[2] = 5; + newBounds[3] = 10; + newBounds[4] = 5; + newBounds[5] = 10; + smallerGeometry->SetBounds(newBounds); + + //legal negative shift + for (unsigned int i = 0; i < 3; ++i) + { + auto legalOrigin = smallerGeometry->GetOrigin(); + legalOrigin[i] -= smallerGeometry->GetSpacing()[i]; + auto legalGeometry = smallerGeometry->Clone(); + legalGeometry->SetOrigin(legalOrigin); + + CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); + } + + //legal positive shift + for (unsigned int i = 0; i < 3; ++i) + { + auto legalOrigin = smallerGeometry->GetOrigin(); + legalOrigin[i] += smallerGeometry->GetSpacing()[i]; + auto legalGeometry = smallerGeometry->Clone(); + legalGeometry->SetOrigin(legalOrigin); + + CPPUNIT_ASSERT(mitk::IsSubGeometry(*legalGeometry, *aDummyGeometry, mitk::eps, true)); + } + + //wrong negative shift + for (unsigned int i = 0; i < 3; ++i) + { + auto wrongOrigin = smallerGeometry->GetOrigin(); + wrongOrigin[i] -= 2 * mitk::eps; + auto wrongGeometry = smallerGeometry->Clone(); + wrongGeometry->SetOrigin(wrongOrigin); + + CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); + } + + //wrong positive shift + for (unsigned int i = 0; i < 3; ++i) + { + auto wrongOrigin = smallerGeometry->GetOrigin(); + wrongOrigin[i] += 2 * mitk::eps; + auto wrongGeometry = smallerGeometry->Clone(); + wrongGeometry->SetOrigin(wrongOrigin); + + CPPUNIT_ASSERT(!mitk::IsSubGeometry(*wrongGeometry, *aDummyGeometry, mitk::eps, true)); + } + } + + /* void (){ DummyTestClass::Pointer dummy = DummyTestClass::New(); CPPUNIT_ASSERT(); //undo changes, new and changed object need to be the same! DummyTestClass::Pointer newDummy = DummyTestClass::New(); CPPUNIT_ASSERT(mitk::Equal(dummy,newDummy,mitk::eps,true)); } */ }; // end class mitkBaseGeometryTestSuite MITK_TEST_SUITE_REGISTRATION(mitkBaseGeometry) diff --git a/Modules/Core/test/mitkItkImageIOTest.cpp b/Modules/Core/test/mitkItkImageIOTest.cpp index 1c27efa749..1f608cc5f2 100644 --- a/Modules/Core/test/mitkItkImageIOTest.cpp +++ b/Modules/Core/test/mitkItkImageIOTest.cpp @@ -1,423 +1,427 @@ /*============================================================================ 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 "mitkException.h" #include #include #include #include "mitkIOUtil.h" #include "mitkITKImageImport.h" #include #include "itksys/SystemTools.hxx" #include #include #include #ifdef WIN32 #include "process.h" #else #include #endif class mitkItkImageIOTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkItkImageIOTestSuite); MITK_TEST(TestImageWriterJpg); MITK_TEST(TestImageWriterPng1); MITK_TEST(TestImageWriterPng2); MITK_TEST(TestImageWriterPng3); MITK_TEST(TestImageWriterSimple); MITK_TEST(TestWrite3DImageWithOnePlane); MITK_TEST(TestWrite3DImageWithTwoPlanes); MITK_TEST(TestWrite3DplusT_ArbitraryTG); MITK_TEST(TestWrite3DplusT_ProportionalTG); CPPUNIT_TEST_SUITE_END(); public: void setUp() override {} void tearDown() override {} void TestImageWriterJpg() { TestImageWriter("NrrdWritingTestImage.jpg"); } void TestImageWriterPng1() { TestImageWriter("Png2D-bw.png"); } void TestImageWriterPng2() { TestImageWriter("RenderingTestData/rgbImage.png"); } void TestImageWriterPng3() { TestImageWriter("RenderingTestData/rgbaImage.png"); } void TestWrite3DplusT_ArbitraryTG() { TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_arbitrary_time_geometry.nrrd"); } void TestWrite3DplusT_ProportionalTG() { TestImageWriter("3D+t-ITKIO-TestData/LinearModel_4D_prop_time_geometry.nrrd"); } void TestImageWriterSimple() { // TODO } std::string AppendExtension(const std::string &filename, const char *extension) { std::string new_filename = filename; new_filename += extension; return new_filename; } bool CompareImageMetaData(mitk::Image::Pointer image, mitk::Image::Pointer reference, bool checkPixelType = true) { // switch to AreIdentical() methods as soon as Bug 11925 (Basic comparison operators) is fixed if (image->GetDimension() != reference->GetDimension()) { MITK_ERROR << "The image dimension differs: IN (" << image->GetDimension() << ") REF(" << reference->GetDimension() << ")"; return false; } // pixel type if (checkPixelType && (image->GetPixelType() != reference->GetPixelType() && image->GetPixelType().GetBitsPerComponent() != reference->GetPixelType().GetBitsPerComponent())) { MITK_ERROR << "Pixeltype differs ( image=" << image->GetPixelType().GetPixelTypeAsString() << "[" << image->GetPixelType().GetBitsPerComponent() << "]" << " reference=" << reference->GetPixelType().GetPixelTypeAsString() << "[" << reference->GetPixelType().GetBitsPerComponent() << "]" << " )"; return false; } return true; } /* Test writing picture formats like *.bmp, *.png, *.tiff or *.jpg NOTE: Saving as picture format must ignore PixelType comparison - not all bits per components are supported (see specification of the format) */ void TestPictureWriting(mitk::Image *image, const std::string &filename, const std::string &extension) { const std::string fullFileName = AppendExtension(filename, extension.c_str()); mitk::Image::Pointer singleSliceImage = nullptr; if (image->GetDimension() == 3) { mitk::ExtractSliceFilter::Pointer extractFilter = mitk::ExtractSliceFilter::New(); extractFilter->SetInput(image); extractFilter->SetWorldGeometry(image->GetSlicedGeometry()->GetPlaneGeometry(0)); extractFilter->Update(); singleSliceImage = extractFilter->GetOutput(); // test 3D writing in format supporting only 2D mitk::IOUtil::Save(image, fullFileName); // test images unsigned int foundImagesCount = 0; // if the image only contains one sinlge slice the itkImageSeriesWriter won't add a number like // filename.XX.extension if (image->GetDimension(2) == 1) { std::stringstream series_filenames; series_filenames << filename << extension; mitk::Image::Pointer compareImage = mitk::IOUtil::Load(series_filenames.str()); if (compareImage.IsNotNull()) { foundImagesCount++; MITK_TEST_CONDITION( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component } remove(series_filenames.str().c_str()); } else // test the whole slice stack { for (unsigned int i = 0; i < image->GetDimension(2); i++) { std::stringstream series_filenames; series_filenames << filename << "." << i + 1 << extension; mitk::Image::Pointer compareImage = mitk::IOUtil::Load(series_filenames.str()); if (compareImage.IsNotNull()) { foundImagesCount++; MITK_TEST_CONDITION( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component } remove(series_filenames.str().c_str()); } } MITK_TEST_CONDITION(foundImagesCount == image->GetDimension(2), "All 2D-Slices of a 3D image were stored correctly."); } else if (image->GetDimension() == 2) { singleSliceImage = image; } // test 2D writing if (singleSliceImage.IsNotNull()) { try { mitk::IOUtil::Save(singleSliceImage, fullFileName); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(fullFileName.c_str()); MITK_TEST_CONDITION_REQUIRED(compareImage.IsNotNull(), "Image stored was succesfully loaded again"); MITK_TEST_CONDITION_REQUIRED( CompareImageMetaData(singleSliceImage, compareImage, false), "Image meta data unchanged after writing and loading again. "); // ignore bits per component remove(fullFileName.c_str()); } catch (itk::ExceptionObject &e) { MITK_TEST_FAILED_MSG(<< "Exception during file writing for ." << extension << ": " << e.what()); } } } /** * test for writing NRRDs */ void TestNRRDWriting(const mitk::Image *image) { CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.nrrd"); tmpStream.close(); try { mitk::IOUtil::Save(image, tmpFilePath); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(tmpFilePath); CPPUNIT_ASSERT_MESSAGE("Image stored in NRRD format was succesfully loaded again", compareImage.IsNotNull()); /*It would make sence to check the images as well (see commented cppunit assert), but currently there seems to be a problem (exception) with most of the test images (partly it seems to be a problem when try to access the pixel content by AccessByItk_1 in mitk::CompareImageDataFilter. This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */ // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE( "TimeGeometries are equal.", mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), mitk::eps, true)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID()); + remove(tmpFilePath.c_str()); } catch (...) { std::remove(tmpFilePath.c_str()); CPPUNIT_FAIL("Exception during NRRD file writing"); } } /** * test for writing MHDs */ void TestMHDWriting(const mitk::Image *image) { CPPUNIT_ASSERT_MESSAGE("Internal error. Passed reference image is null.", image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mhd"); tmpStream.close(); std::string tmpFilePathWithoutExt = tmpFilePath.substr(0, tmpFilePath.size() - 4); try { mitk::IOUtil::Save(image, tmpFilePath); mitk::Image::Pointer compareImage = mitk::IOUtil::Load(tmpFilePath); CPPUNIT_ASSERT_MESSAGE("Image stored in MHD format was succesfully loaded again! ", compareImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE(".mhd file exists", itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".mhd").c_str())); CPPUNIT_ASSERT_MESSAGE(".raw or .zraw exists", itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".raw").c_str()) || itksys::SystemTools::FileExists((tmpFilePathWithoutExt + ".zraw").c_str())); /*It would make sence to check the images as well (see commented cppunit assert), but currently there seems to be a problem (exception) with most of the test images (partly it seems to be a problem when try to access the pixel content by AccessByItk_1 in mitk::CompareImageDataFilter. This problem should be dealt with in Bug 19533 - mitkITKImageIOTest needs improvement */ // CPPUNIT_ASSERT_MESSAGE("Images are equal.", mitk::Equal(*image, *compareImage, mitk::eps, true)); CPPUNIT_ASSERT_MESSAGE("TimeGeometries are equal.", mitk::Equal(*(image->GetTimeGeometry()), *(compareImage->GetTimeGeometry()), 5e-4, true)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", image->GetUID(), compareImage->GetUID()); + // delete remove(tmpFilePath.c_str()); remove((tmpFilePathWithoutExt + ".raw").c_str()); remove((tmpFilePathWithoutExt + ".zraw").c_str()); } catch (...) { CPPUNIT_FAIL("Exception during.mhd file writing"); } } /** * test for "ImageWriter". * * argc and argv are the command line parameters which were passed to * the ADD_TEST command in the CMakeLists.txt file. For the automatic * tests, argv is either empty for the simple tests or contains the filename * of a test image for the image tests (see CMakeLists.txt). */ void TestImageWriter(std::string sourcefile) { sourcefile = GetTestDataFilePath(sourcefile); // load image CPPUNIT_ASSERT_MESSAGE("Checking whether source image exists", itksys::SystemTools::FileExists(sourcefile.c_str())); mitk::Image::Pointer image = nullptr; try { image = mitk::IOUtil::Load(sourcefile); } catch (...) { CPPUNIT_FAIL("Exception during file loading:"); } CPPUNIT_ASSERT_MESSAGE("loaded image not nullptr", image.IsNotNull()); // write ITK .mhd image (2D and 3D only) if (image->GetDimension() <= 3) { TestMHDWriting(image); } // testing more component image writing as nrrd files TestNRRDWriting(image); std::ofstream tmpStream; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX"); tmpStream.close(); TestPictureWriting(image, tmpFilePath, ".png"); TestPictureWriting(image, tmpFilePath, ".jpg"); TestPictureWriting(image, tmpFilePath, ".tiff"); TestPictureWriting(image, tmpFilePath, ".bmp"); // always end with this! } /** * Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes) */ void TestWrite3DImageWithOnePlane() { typedef itk::Image ImageType; ImageType::Pointer itkImage = ImageType::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size[0] = 100; size[1] = 100; size[2] = 1; ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); itkImage->SetRegions(region); itkImage->Allocate(); itkImage->FillBuffer(0); itk::ImageRegionIterator imageIterator(itkImage, itkImage->GetLargestPossibleRegion()); // Make two squares while (!imageIterator.IsAtEnd()) { if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) && (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20)) { imageIterator.Set(255); } if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) && (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70)) { imageIterator.Set(60); } ++imageIterator; } mitk::Image::Pointer image = mitk::ImportItkImage(itkImage); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd")); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")); } /** * Try to write a 3D image with only one plane (a 2D images in disguise for all intents and purposes) */ void TestWrite3DImageWithTwoPlanes() { typedef itk::Image ImageType; ImageType::Pointer itkImage = ImageType::New(); ImageType::IndexType start; start.Fill(0); ImageType::SizeType size; size[0] = 100; size[1] = 100; size[2] = 2; ImageType::RegionType region; region.SetSize(size); region.SetIndex(start); itkImage->SetRegions(region); itkImage->Allocate(); itkImage->FillBuffer(0); itk::ImageRegionIterator imageIterator(itkImage, itkImage->GetLargestPossibleRegion()); // Make two squares while (!imageIterator.IsAtEnd()) { if ((imageIterator.GetIndex()[0] > 5 && imageIterator.GetIndex()[0] < 20) && (imageIterator.GetIndex()[1] > 5 && imageIterator.GetIndex()[1] < 20)) { imageIterator.Set(255); } if ((imageIterator.GetIndex()[0] > 50 && imageIterator.GetIndex()[0] < 70) && (imageIterator.GetIndex()[1] > 50 && imageIterator.GetIndex()[1] < 70)) { imageIterator.Set(60); } ++imageIterator; } mitk::Image::Pointer image = mitk::ImportItkImage(itkImage); mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.nrrd")); CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(image, mitk::IOUtil::CreateTemporaryFile("3Dto2DTestImageXXXXXX.png")), mitk::Exception); } }; MITK_TEST_SUITE_REGISTRATION(mitkItkImageIO) diff --git a/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp b/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp new file mode 100644 index 0000000000..e931e20e27 --- /dev/null +++ b/Modules/Core/test/mitkNodePredicateSubGeometryTest.cpp @@ -0,0 +1,243 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkGeometry3D.h" +#include "mitkBaseDataTestImplementation.h" +#include "mitkNodePredicateSubGeometry.h" +#include "mitkDataNode.h" +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" + +class mitkNodePredicateSubGeometryTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkNodePredicateSubGeometryTestSuite); + MITK_TEST(Check_InvalidConstructor); + MITK_TEST(Check_Spacing); + MITK_TEST(Check_TransformMatrix); + MITK_TEST(Check_Bounds); + MITK_TEST(Check_Grid); + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::BaseDataTestImplementation::Pointer m_Data; + mitk::DataNode::Pointer m_Node; + + mitk::Geometry3D::Pointer m_RefGeometry; + + mitk::Geometry3D::Pointer m_AnotherGeometry3D; + +public: + /** +* @brief Setup Always call this method before each Test-case to ensure correct and new intialization of the used members +* for a new test case. (If the members are not used in a test, the method does not need to be called). +*/ + void setUp() override + { + m_RefGeometry = mitk::Geometry3D::New(); + m_RefGeometry->Initialize(); + + m_Data = mitk::BaseDataTestImplementation::New(); + m_Data->SetClonedGeometry(m_RefGeometry); + + m_Node = mitk::DataNode::New(); + m_Node->SetData(m_Data); + + m_AnotherGeometry3D = m_RefGeometry->Clone(); + } + + void tearDown() override + { + m_RefGeometry = nullptr; + m_AnotherGeometry3D = nullptr; + m_Data = nullptr; + } + + void Check_InvalidConstructor() + { + m_RefGeometry = nullptr; + CPPUNIT_ASSERT_THROW(mitk::NodePredicateSubGeometry::New(m_RefGeometry, 3), mitk::Exception); + CPPUNIT_ASSERT_THROW(mitk::NodePredicateSubGeometry::New(m_RefGeometry), mitk::Exception); + } + + void Check_CloneAndOriginal() + { + mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); + + CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); + } + + void Check_Spacing() + { + mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); + + for (unsigned int i = 0; i < 3; ++i) + { + mitk::Vector3D wrongSpacing = m_RefGeometry->GetSpacing(); + wrongSpacing[i] += mitk::eps * 2; + auto wrongGeometry = m_RefGeometry->Clone(); + wrongGeometry->SetSpacing(wrongSpacing); + m_Node->GetData()->SetGeometry(wrongGeometry); + + CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); + } + for (unsigned int i = 0; i < 3; ++i) + { + mitk::Vector3D wrongSpacing = m_RefGeometry->GetSpacing(); + wrongSpacing[i] -= mitk::eps * 2; + auto wrongGeometry = m_RefGeometry->Clone(); + wrongGeometry->SetSpacing(wrongSpacing); + m_Node->GetData()->SetGeometry(wrongGeometry); + + CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); + } + } + + void Check_TransformMatrix() + { + mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); + + for (unsigned int i = 0; i < 3; ++i) + { + for (unsigned int j = 0; j < 3; ++j) + { + itk::Matrix wrongMatrix = m_RefGeometry->GetIndexToWorldTransform()->GetMatrix(); + wrongMatrix[i][j] += mitk::eps * 2; + auto wrongGeometry = m_RefGeometry->Clone(); + wrongGeometry->GetIndexToWorldTransform()->SetMatrix(wrongMatrix); + m_Node->GetData()->SetGeometry(wrongGeometry); + + CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); + } + } + } + + void Check_Bounds() + { + auto newBounds = m_RefGeometry->GetBounds(); + newBounds[0] = 10; + newBounds[1] = 20; + newBounds[2] = 10; + newBounds[3] = 20; + newBounds[4] = 10; + newBounds[5] = 20; + m_RefGeometry->SetBounds(newBounds); + mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); + + for (unsigned int i = 0; i < 6; ++i) + { + auto legalBounds = newBounds; + if (i % 2 == 0) + { + legalBounds[i] += 1; + } + else + { + legalBounds[i] -= 1; + } + auto legalGeometry = m_RefGeometry->Clone(); + legalGeometry->SetBounds(legalBounds); + m_Node->GetData()->SetGeometry(legalGeometry); + + CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); + } + + for (unsigned int i = 0; i < 6; ++i) + { + auto wrongBounds = m_RefGeometry->GetBounds(); + if (i % 2 == 0) + { + wrongBounds[i] -= 1; + } + else + { + wrongBounds[i] += 1; + } + auto wrongGeometry = m_RefGeometry->Clone(); + wrongGeometry->SetBounds(wrongBounds); + m_Node->GetData()->SetGeometry(wrongGeometry); + + CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); + } + } + + void Check_Grid() + { + auto newBounds = m_RefGeometry->GetBounds(); + newBounds[0] = 0; + newBounds[1] = 20; + newBounds[2] = 0; + newBounds[3] = 20; + newBounds[4] = 0; + newBounds[5] = 20; + m_RefGeometry->SetBounds(newBounds); + mitk::NodePredicateSubGeometry::Pointer predicate = mitk::NodePredicateSubGeometry::New(m_RefGeometry); + + auto smallerGeometry = m_RefGeometry->Clone(); + newBounds[0] = 5; + newBounds[1] = 10; + newBounds[2] = 5; + newBounds[3] = 10; + newBounds[4] = 5; + newBounds[5] = 10; + smallerGeometry->SetBounds(newBounds); + + //legal negative shift + for (unsigned int i = 0; i < 3; ++i) + { + auto legalOrigin = smallerGeometry->GetOrigin(); + legalOrigin[i] -= smallerGeometry->GetSpacing()[i]; + auto legalGeometry = smallerGeometry->Clone(); + legalGeometry->SetOrigin(legalOrigin); + m_Node->GetData()->SetGeometry(legalGeometry); + + CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); + } + + //legal positive shift + for (unsigned int i = 0; i < 3; ++i) + { + auto legalOrigin = smallerGeometry->GetOrigin(); + legalOrigin[i] += smallerGeometry->GetSpacing()[i]; + auto legalGeometry = smallerGeometry->Clone(); + legalGeometry->SetOrigin(legalOrigin); + m_Node->GetData()->SetGeometry(legalGeometry); + + CPPUNIT_ASSERT(predicate->CheckNode(m_Node)); + } + + //wrong negative shift + for (unsigned int i = 0; i < 3; ++i) + { + auto wrongOrigin = smallerGeometry->GetOrigin(); + wrongOrigin[i] -= 2 * mitk::eps; + auto wrongGeometry = smallerGeometry->Clone(); + wrongGeometry->SetOrigin(wrongOrigin); + m_Node->GetData()->SetGeometry(wrongGeometry); + + CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); + } + + //wrong positive shift + for (unsigned int i = 0; i < 3; ++i) + { + auto wrongOrigin = smallerGeometry->GetOrigin(); + wrongOrigin[i] += 2 * mitk::eps; + auto wrongGeometry = smallerGeometry->Clone(); + wrongGeometry->SetOrigin(wrongOrigin); + m_Node->GetData()->SetGeometry(wrongGeometry); + + CPPUNIT_ASSERT(!predicate->CheckNode(m_Node)); + } + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkNodePredicateSubGeometry) diff --git a/Modules/Core/test/mitkUIDGeneratorTest.cpp b/Modules/Core/test/mitkUIDGeneratorTest.cpp index 12dd6aa337..b2b735beae 100644 --- a/Modules/Core/test/mitkUIDGeneratorTest.cpp +++ b/Modules/Core/test/mitkUIDGeneratorTest.cpp @@ -1,65 +1,55 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // Testing #include "mitkTestFixture.h" #include "mitkTestingMacros.h" // std includes #include // MITK includes #include "mitkUIDGenerator.h" #include #include #include class mitkUIDGeneratorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkUIDGeneratorTestSuite); MITK_TEST(UIDGeneratorInstanceRenewalSucceed); MITK_TEST(UIDGeneratorMultipleInstancesSucceed); CPPUNIT_TEST_SUITE_END(); - unsigned short m_UidLengthStart = 5; - unsigned short m_UidLengthEnd = 20; - - void UIDGeneratorInstanceRenewalSucceed() { - for (auto k = m_UidLengthStart; k < m_UidLengthEnd; ++k) - { - mitk::UIDGenerator uidGen1("UID_", k); - auto uid1_1 = uidGen1.GetUID(); + mitk::UIDGenerator uidGen1("UID_"); + auto uid1_1 = uidGen1.GetUID(); - uidGen1 = mitk::UIDGenerator("UID_", k); - auto uid2_1 = uidGen1.GetUID(); + uidGen1 = mitk::UIDGenerator("UID_"); + auto uid2_1 = uidGen1.GetUID(); - CPPUNIT_ASSERT_MESSAGE("Different UIDs are not allowed to be equal", uid1_1 != uid2_1); - } + CPPUNIT_ASSERT_MESSAGE("Different UIDs are not allowed to be equal", uid1_1 != uid2_1); } void UIDGeneratorMultipleInstancesSucceed() { - for (auto k = m_UidLengthStart; k < m_UidLengthEnd; ++k) - { - mitk::UIDGenerator uidGen1("UID_", k); - mitk::UIDGenerator uidGen2("UID_", k); + mitk::UIDGenerator uidGen1("UID_"); + mitk::UIDGenerator uidGen2("UID_"); - auto uid1_1 = uidGen1.GetUID(); - auto uid2_1 = uidGen2.GetUID(); + auto uid1_1 = uidGen1.GetUID(); + auto uid2_1 = uidGen2.GetUID(); - CPPUNIT_ASSERT_MESSAGE("Different UIDs are not allowed to be equal", uid1_1 != uid2_1); - } + CPPUNIT_ASSERT_MESSAGE("Different UIDs are not allowed to be equal", uid1_1 != uid2_1); } }; MITK_TEST_SUITE_REGISTRATION(mitkUIDGenerator) diff --git a/Modules/IGT/DataManagement/mitkNavigationDataSource.cpp b/Modules/IGT/DataManagement/mitkNavigationDataSource.cpp index 68d1636143..20bcf5d235 100644 --- a/Modules/IGT/DataManagement/mitkNavigationDataSource.cpp +++ b/Modules/IGT/DataManagement/mitkNavigationDataSource.cpp @@ -1,165 +1,165 @@ /*============================================================================ 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 "mitkNavigationDataSource.h" #include "mitkUIDGenerator.h" //Microservices #include #include #include #include const std::string mitk::NavigationDataSource::US_INTERFACE_NAME = "org.mitk.services.NavigationDataSource"; const std::string mitk::NavigationDataSource::US_PROPKEY_DEVICENAME = US_INTERFACE_NAME + ".devicename"; const std::string mitk::NavigationDataSource::US_PROPKEY_ID = US_INTERFACE_NAME + ".id"; const std::string mitk::NavigationDataSource::US_PROPKEY_ISACTIVE = US_INTERFACE_NAME + ".isActive"; mitk::NavigationDataSource::NavigationDataSource() : itk::ProcessObject(), m_Name("NavigationDataSource (no defined type)"), m_IsFrozen(false), m_ToolMetaDataCollection(mitk::NavigationToolStorage::New()) { } mitk::NavigationDataSource::~NavigationDataSource() { } mitk::NavigationData* mitk::NavigationDataSource::GetOutput() { if (this->GetNumberOfIndexedOutputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetPrimaryOutput()); } mitk::NavigationData* mitk::NavigationDataSource::GetOutput(DataObjectPointerArraySizeType idx) { NavigationData* out = dynamic_cast( this->ProcessObject::GetOutput(idx) ); if ( out == nullptr && this->ProcessObject::GetOutput(idx) != nullptr ) { itkWarningMacro (<< "Unable to convert output number " << idx << " to type " << typeid( NavigationData ).name () ); } return out; } mitk::NavigationData* mitk::NavigationDataSource::GetOutput(const std::string& navDataName) { DataObjectPointerArray outputs = this->GetOutputs(); for (DataObjectPointerArray::iterator it = outputs.begin(); it != outputs.end(); ++it) if (navDataName == (static_cast(it->GetPointer()))->GetName()) return static_cast(it->GetPointer()); return nullptr; } itk::ProcessObject::DataObjectPointerArraySizeType mitk::NavigationDataSource::GetOutputIndex( std::string navDataName ) { DataObjectPointerArray outputs = this->GetOutputs(); for (DataObjectPointerArray::size_type i = 0; i < outputs.size(); ++i) if (navDataName == (static_cast(outputs.at(i).GetPointer()))->GetName()) return i; throw std::invalid_argument("output name does not exist"); } void mitk::NavigationDataSource::RegisterAsMicroservice(){ // Get Context us::ModuleContext* context = us::GetModuleContext(); // Define ServiceProps us::ServiceProperties props; - mitk::UIDGenerator uidGen = mitk::UIDGenerator ("org.mitk.services.NavigationDataSource.id_", 16); + mitk::UIDGenerator uidGen = mitk::UIDGenerator ("org.mitk.services.NavigationDataSource.id_"); props[ US_PROPKEY_ID ] = uidGen.GetUID(); props[ US_PROPKEY_DEVICENAME ] = m_Name; m_ServiceRegistration = context->RegisterService(this, props); } void mitk::NavigationDataSource::UnRegisterMicroservice(){ if (m_ServiceRegistration != nullptr) m_ServiceRegistration.Unregister(); m_ServiceRegistration = 0; } std::string mitk::NavigationDataSource::GetMicroserviceID(){ return this->m_ServiceRegistration.GetReference().GetProperty(US_PROPKEY_ID).ToString(); } void mitk::NavigationDataSource::GraftOutput(itk::DataObject *graft) { this->GraftNthOutput(0, graft); } void mitk::NavigationDataSource::GraftNthOutput(unsigned int idx, itk::DataObject *graft) { if ( idx >= this->GetNumberOfIndexedOutputs() ) { itkExceptionMacro(<<"Requested to graft output " << idx << " but this filter only has " << this->GetNumberOfIndexedOutputs() << " Outputs."); } if ( !graft ) { itkExceptionMacro(<<"Requested to graft output with a nullptr pointer object" ); } itk::DataObject* output = this->GetOutput(idx); if ( !output ) { itkExceptionMacro(<<"Requested to graft output that is a nullptr pointer" ); } // Call Graft on NavigationData to copy member data output->Graft( graft ); } itk::DataObject::Pointer mitk::NavigationDataSource::MakeOutput ( DataObjectPointerArraySizeType /*idx*/ ) { return mitk::NavigationData::New().GetPointer(); } itk::DataObject::Pointer mitk::NavigationDataSource::MakeOutput( const DataObjectIdentifierType & name ) { itkDebugMacro("MakeOutput(" << name << ")"); if( this->IsIndexedOutputName(name) ) { return this->MakeOutput( this->MakeIndexFromOutputName(name) ); } return static_cast(mitk::NavigationData::New().GetPointer()); } mitk::PropertyList::ConstPointer mitk::NavigationDataSource::GetParameters() const { mitk::PropertyList::Pointer p = mitk::PropertyList::New(); // add properties to p like this: //p->SetProperty("MyFilter_MyParameter", mitk::PropertyDataType::New(m_MyParameter)); return mitk::PropertyList::ConstPointer(p); } void mitk::NavigationDataSource::Freeze() { m_IsFrozen = true; } void mitk::NavigationDataSource::UnFreeze() { m_IsFrozen = false; } mitk::NavigationTool::Pointer mitk::NavigationDataSource::GetToolMetaData(DataObjectPointerArraySizeType idx) { if (idx >= this->GetNumberOfIndexedOutputs()) { return mitk::NavigationTool::New(); } else { return GetToolMetaData(this->GetOutput(idx)->GetName()); } } mitk::NavigationTool::Pointer mitk::NavigationDataSource::GetToolMetaData(const std::string& navDataName) { mitk::NavigationTool::Pointer returnValue = m_ToolMetaDataCollection->GetToolByName(navDataName); if (returnValue == nullptr) { returnValue = mitk::NavigationTool::New(); } return returnValue; } diff --git a/Modules/IGT/DataManagement/mitkNavigationTool.cpp b/Modules/IGT/DataManagement/mitkNavigationTool.cpp index 1af990ee8f..cbea5641c1 100644 --- a/Modules/IGT/DataManagement/mitkNavigationTool.cpp +++ b/Modules/IGT/DataManagement/mitkNavigationTool.cpp @@ -1,358 +1,358 @@ /*============================================================================ 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 "mitkNavigationTool.h" #include "mitkIGTException.h" #include "mitkNavigationData.h" #include "Poco/File.h" #include "mitkUnspecifiedTrackingTypeInformation.h" #include #include "vtkConeSource.h" #include "vtkLineSource.h" #include "vtkCylinderSource.h" #include "vtkTransformPolyDataFilter.h" #include #include "mitkTextAnnotation3D.h" #include "mitkManualPlacementAnnotationRenderer.h" #include "mitkBaseRenderer.h" mitk::NavigationTool::NavigationTool() : m_Identifier("None"), m_Type(mitk::NavigationTool::Unknown), m_CalibrationFile("none"), m_SerialNumber(""), m_TrackingDeviceType(mitk::UnspecifiedTrackingTypeInformation::GetTrackingDeviceName()), m_ToolLandmarks(mitk::PointSet::New()), m_ToolControlPoints(mitk::PointSet::New()), m_ToolAxisOrientation(mitk::Quaternion(0, 0, 0, 1)) { m_ToolTipPosition[0] = 0; m_ToolTipPosition[1] = 0; m_ToolTipPosition[2] = 0; SetDefaultSurface(); } itk::LightObject::Pointer mitk::NavigationTool::InternalClone() const { Self::Pointer tool = new Self(*this); tool->UnRegister(); return tool.GetPointer(); } mitk::NavigationTool::NavigationTool(const NavigationTool &other) : Superclass() { this->m_Identifier = other.m_Identifier; this->m_Type = other.m_Type; if (other.m_DataNode.IsNotNull()) { - this->m_DataNode = other.m_DataNode->Clone(); + this->m_DataNode = mitk::DataNode::New(); this->m_DataNode->SetName(other.m_DataNode->GetName()); if (other.m_DataNode->GetData()) { this->m_DataNode->SetData(dynamic_cast(other.m_DataNode->GetData()->Clone().GetPointer())); } } if (other.m_SpatialObject.IsNotNull()) this->m_SpatialObject = other.m_SpatialObject->Clone(); this->m_CalibrationFile = other.m_CalibrationFile; this->m_SerialNumber = other.m_SerialNumber; this->m_TrackingDeviceType = other.m_TrackingDeviceType; if (other.m_ToolLandmarks.IsNotNull()) this->m_ToolLandmarks = other.m_ToolLandmarks->Clone(); if (other.m_ToolControlPoints.IsNotNull()) this->m_ToolControlPoints = other.m_ToolControlPoints->Clone(); this->m_ToolTipPosition = other.m_ToolTipPosition; this->m_ToolAxisOrientation = other.m_ToolAxisOrientation; } mitk::NavigationTool::~NavigationTool() { } mitk::Point3D mitk::NavigationTool::GetToolAxis() { // The tool axis in the sensor coordinate system is defined as the negative z-axis mitk::Vector3D toolAxisSensorCoordinateSystem; mitk::FillVector3D(toolAxisSensorCoordinateSystem, 0.0, 0.0, -1.0); // Apply inverse tool axis transform to calculate tool axis vnl_vector_fixed toolAxisVector = m_ToolAxisOrientation.inverse().rotate(toolAxisSensorCoordinateSystem.GetVnlVector()); // Transfer to mitk::Point3D mitk::Point3D toolAxis; toolAxis[0] = toolAxisVector[0]; toolAxis[1] = toolAxisVector[1]; toolAxis[2] = toolAxisVector[2]; return toolAxis; } void mitk::NavigationTool::SetToolAxis(mitk::Point3D toolAxis) { // The tool axis in the sensor coordinate system is defined as the negative z-axis mitk::Vector3D toolAxisSensorCoordinateSystem; mitk::FillVector3D(toolAxisSensorCoordinateSystem, 0.0, 0.0, -1.0); // Normalize the tool axis as obtained by a tool axis calibration mitk::Vector3D toolAxisFromCalibration; mitk::FillVector3D(toolAxisFromCalibration, toolAxis[0], toolAxis[1], toolAxis[2]); toolAxisFromCalibration.Normalize(); // if tool axis to be set is different to the default tool axis (0,0,-1) calculate the tool axis orientation, otherwise set it to identity if (toolAxisSensorCoordinateSystem == toolAxisFromCalibration) { m_ToolAxisOrientation = mitk::Quaternion(0,0,0,1); } else { // Determine rotation angle mitk::ScalarType rotationAngle = acos(toolAxisSensorCoordinateSystem*toolAxisFromCalibration); // Determine rotation axis mitk::Vector3D rotationAxis = itk::CrossProduct(toolAxisSensorCoordinateSystem, toolAxisFromCalibration); // Calculate transform itk::AffineTransform::Pointer sensorToToolAxisOrientation = itk::AffineTransform::New(); sensorToToolAxisOrientation->Rotate3D(rotationAxis, rotationAngle); // transfer to quaternion notation. Note that the vnl_quaternion expects the matrix in row major format, hence the transpose mitk::Quaternion toolAxisTransform(sensorToToolAxisOrientation->GetMatrix().GetVnlMatrix().transpose()); // Update the tool axis orientation m_ToolAxisOrientation = toolAxisTransform; } } mitk::AffineTransform3D::Pointer mitk::NavigationTool::GetToolTipTransform() { mitk::NavigationData::Pointer returnValue = mitk::NavigationData::New(); returnValue->SetPosition(this->m_ToolTipPosition); returnValue->SetOrientation(this->m_ToolAxisOrientation); return returnValue->GetAffineTransform3D(); } void mitk::NavigationTool::Graft(const DataObject *data) { // Attempt to cast data to an NavigationData const Self* nd; try { nd = dynamic_cast(data); } catch (...) { mitkThrowException(mitk::IGTException) << "mitk::NavigationData::Graft cannot cast " << typeid(data).name() << " to " << typeid(const Self *).name(); } if (!nd) { // pointer could not be cast back down mitkThrowException(mitk::IGTException) << "mitk::NavigationData::Graft cannot cast " << typeid(data).name() << " to " << typeid(const Self *).name(); } // Now copy anything that is needed m_Identifier = nd->GetIdentifier(); m_Type = nd->GetType(); m_DataNode->SetName(nd->GetDataNode()->GetName()); m_DataNode->SetData(nd->GetDataNode()->GetData()); m_SpatialObject = nd->GetSpatialObject(); m_CalibrationFile = nd->GetCalibrationFile(); m_SerialNumber = nd->GetSerialNumber(); m_TrackingDeviceType = nd->GetTrackingDeviceType(); m_ToolLandmarks = nd->GetToolLandmarks(); m_ToolControlPoints = nd->GetToolControlPoints(); m_ToolTipPosition = nd->GetToolTipPosition(); m_ToolAxisOrientation = nd->GetToolAxisOrientation(); } bool mitk::NavigationTool::IsToolTipSet() { if ((m_ToolTipPosition[0] == 0) && (m_ToolTipPosition[1] == 0) && (m_ToolTipPosition[2] == 0) && (m_ToolAxisOrientation.x() == 0) && (m_ToolAxisOrientation.y() == 0) && (m_ToolAxisOrientation.z() == 0) && (m_ToolAxisOrientation.r() == 1)) return false; else return true; } void mitk::NavigationTool::SetCalibrationFile(const std::string filename) { //check if file does exist: if (filename == "") { m_CalibrationFile = "none"; } else { Poco::File myFile(filename); if (myFile.exists()) m_CalibrationFile = filename; else m_CalibrationFile = "none"; } } std::string mitk::NavigationTool::GetToolName() { if (this->m_DataNode.IsNull()) { return ""; } else { return m_DataNode->GetName(); } } mitk::Surface::Pointer mitk::NavigationTool::GetToolSurface() { if (this->m_DataNode.IsNull()) { return nullptr; } else if (this->m_DataNode->GetData() == nullptr) { return nullptr; } else { return dynamic_cast(m_DataNode->GetData()); } } void mitk::NavigationTool::SetDefaultSurface() { if (m_DataNode.IsNull()) m_DataNode = mitk::DataNode::New(); mitk::Surface::Pointer mySphere = mitk::Surface::New(); double axisLength = 5.; vtkSmartPointer vtkSphere = vtkSmartPointer::New(); vtkSmartPointer vtkCone = vtkSmartPointer::New(); vtkSmartPointer vtkCylinder = vtkSmartPointer::New(); vtkSmartPointer axis = vtkSmartPointer::New(); vtkSmartPointer vtkLine = vtkSmartPointer::New(); vtkSmartPointer vtkLine2 = vtkSmartPointer::New(); vtkSmartPointer vtkLine3 = vtkSmartPointer::New(); vtkSmartPointer appendPolyData = vtkSmartPointer::New(); vtkSmartPointer surface = vtkSmartPointer::New(); //Y-Axis (start with y, cause cylinder is oriented in y by vtk default...) vtkCone->SetDirection(0, 1, 0); vtkCone->SetHeight(1.0); vtkCone->SetRadius(0.4f); vtkCone->SetResolution(16); vtkCone->SetCenter(0.0, axisLength, 0.0); vtkCone->Update(); vtkCylinder->SetRadius(0.05); vtkCylinder->SetHeight(axisLength); vtkCylinder->SetCenter(0.0, 0.5*axisLength, 0.0); vtkCylinder->Update(); appendPolyData->AddInputData(vtkCone->GetOutput()); appendPolyData->AddInputData(vtkCylinder->GetOutput()); appendPolyData->Update(); axis->DeepCopy(appendPolyData->GetOutput()); //y symbol vtkLine->SetPoint1(-0.5, axisLength + 2., 0.0); vtkLine->SetPoint2(0.0, axisLength + 1.5, 0.0); vtkLine->Update(); vtkLine2->SetPoint1(0.5, axisLength + 2., 0.0); vtkLine2->SetPoint2(-0.5, axisLength + 1., 0.0); vtkLine2->Update(); appendPolyData->AddInputData(vtkLine->GetOutput()); appendPolyData->AddInputData(vtkLine2->GetOutput()); appendPolyData->AddInputData(axis); appendPolyData->Update(); surface->DeepCopy(appendPolyData->GetOutput()); //X-axis vtkSmartPointer XTransform = vtkSmartPointer::New(); XTransform->RotateZ(-90); vtkSmartPointer TrafoFilter = vtkSmartPointer::New(); TrafoFilter->SetTransform(XTransform); TrafoFilter->SetInputData(axis); TrafoFilter->Update(); //x symbol vtkLine->SetPoint1(axisLength + 2., -0.5, 0.0); vtkLine->SetPoint2(axisLength + 1., 0.5, 0.0); vtkLine->Update(); vtkLine2->SetPoint1(axisLength + 2., 0.5, 0.0); vtkLine2->SetPoint2(axisLength + 1., -0.5, 0.0); vtkLine2->Update(); appendPolyData->AddInputData(vtkLine->GetOutput()); appendPolyData->AddInputData(vtkLine2->GetOutput()); appendPolyData->AddInputData(TrafoFilter->GetOutput()); appendPolyData->AddInputData(surface); appendPolyData->Update(); surface->DeepCopy(appendPolyData->GetOutput()); //Z-axis vtkSmartPointer ZTransform = vtkSmartPointer::New(); ZTransform->RotateX(90); TrafoFilter->SetTransform(ZTransform); TrafoFilter->SetInputData(axis); TrafoFilter->Update(); //z symbol vtkLine->SetPoint1(-0.5, 0.0, axisLength + 2.); vtkLine->SetPoint2(0.5, 0.0, axisLength + 2.); vtkLine->Update(); vtkLine2->SetPoint1(-0.5, 0.0, axisLength + 2.); vtkLine2->SetPoint2(0.5, 0.0, axisLength + 1.); vtkLine2->Update(); vtkLine3->SetPoint1(0.5, 0.0, axisLength + 1.); vtkLine3->SetPoint2(-0.5, 0.0, axisLength + 1.); vtkLine3->Update(); appendPolyData->AddInputData(vtkLine->GetOutput()); appendPolyData->AddInputData(vtkLine2->GetOutput()); appendPolyData->AddInputData(vtkLine3->GetOutput()); appendPolyData->AddInputData(TrafoFilter->GetOutput()); appendPolyData->AddInputData(surface); appendPolyData->Update(); surface->DeepCopy(appendPolyData->GetOutput()); //Center vtkSphere->SetRadius(0.5f); vtkSphere->SetCenter(0.0, 0.0, 0.0); vtkSphere->Update(); appendPolyData->AddInputData(vtkSphere->GetOutput()); appendPolyData->AddInputData(surface); appendPolyData->Update(); surface->DeepCopy(appendPolyData->GetOutput()); //Scale vtkSmartPointer ScaleTransform = vtkSmartPointer::New(); ScaleTransform->Scale(20., 20., 20.); TrafoFilter->SetTransform(ScaleTransform); TrafoFilter->SetInputData(surface); TrafoFilter->Update(); mySphere->SetVtkPolyData(TrafoFilter->GetOutput()); this->GetDataNode()->SetData(mySphere); } std::string mitk::NavigationTool::GetStringWithAllToolInformation() const { std::stringstream _info; _info << " Identifier: " << this->m_Identifier << "\n" << " NavigationToolType: " << m_Type << "\n" << " Calibration file: " << m_CalibrationFile << "\n" << " Serial number: " << m_SerialNumber << "\n" << " TrackingDeviceType: " << m_TrackingDeviceType << "\n" << " ToolTip Position: " << m_ToolTipPosition << "\n" << " Tool Axis Orientation: " << m_ToolAxisOrientation << "\n" << " Tool Axis: " << m_ToolAxisOrientation.inverse().rotate(vnl_vector_fixed(0.0,0.0,-1.0)) ; return _info.str(); } diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp index e06482a23d..164906747a 100644 --- a/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp +++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsCalculatorTest.cpp @@ -1,1139 +1,1139 @@ /*============================================================================ 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 "mitkImageStatisticsCalculator.h" #include #include #include #include #include #include #include /** * \brief Test class for mitkImageStatisticsCalculator * * This test covers: * - instantiation of an ImageStatisticsCalculator class * - correctness of statistics when using PlanarFigures for masking */ class mitkImageStatisticsCalculatorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkImageStatisticsCalculatorTestSuite); MITK_TEST(TestUninitializedImage); MITK_TEST(TestCase1); MITK_TEST(TestCase2); MITK_TEST(TestCase3); MITK_TEST(TestCase4); MITK_TEST(TestCase5); MITK_TEST(TestCase6); MITK_TEST(TestCase7); MITK_TEST(TestCase8); MITK_TEST(TestCase9); MITK_TEST(TestCase10); MITK_TEST(TestCase11); MITK_TEST(TestCase12); MITK_TEST(TestPic3DCroppedNoMask); MITK_TEST(TestPic3DCroppedBinMask); MITK_TEST(TestPic3DCroppedMultilabelMask); MITK_TEST(TestPic3DCroppedPlanarFigure); MITK_TEST(TestUS4DCroppedNoMaskTimeStep1); MITK_TEST(TestUS4DCroppedBinMaskTimeStep1); MITK_TEST(TestUS4DCroppedMultilabelMaskTimeStep1); MITK_TEST(TestUS4DCroppedPlanarFigureTimeStep1); MITK_TEST(TestUS4DCroppedAllTimesteps); MITK_TEST(TestUS4DCropped3DMask); CPPUNIT_TEST_SUITE_END(); public: void TestUninitializedImage(); void TestCase1(); void TestCase2(); void TestCase3(); void TestCase4(); void TestCase5(); void TestCase6(); void TestCase7(); void TestCase8(); void TestCase9(); void TestCase10(); void TestCase11(); void TestCase12(); void TestPic3DCroppedNoMask(); void TestPic3DCroppedBinMask(); void TestPic3DCroppedMultilabelMask(); void TestPic3DCroppedPlanarFigure(); void TestUS4DCroppedNoMaskTimeStep1(); void TestUS4DCroppedBinMaskTimeStep1(); void TestUS4DCroppedMultilabelMaskTimeStep1(); void TestUS4DCroppedPlanarFigureTimeStep1(); void TestUS4DCroppedAllTimesteps(); void TestUS4DCropped3DMask(); private: mitk::Image::ConstPointer m_TestImage; mitk::Image::ConstPointer m_Pic3DCroppedImage; mitk::Image::Pointer m_Pic3DCroppedBinMask; mitk::Image::Pointer m_Pic3DCroppedMultilabelMask; mitk::PlanarFigure::Pointer m_Pic3DCroppedPlanarFigure; mitk::Image::ConstPointer m_US4DCroppedImage; mitk::Image::Pointer m_US4DCroppedBinMask; mitk::Image::Pointer m_US4DCroppedMultilabelMask; mitk::Image::Pointer m_US4DCropped3DBinMask; mitk::PlanarFigure::Pointer m_US4DCroppedPlanarFigure; mitk::PlaneGeometry::Pointer m_Geometry; // creates a polygon given a geometry and a vector of 2d points mitk::PlanarPolygon::Pointer GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector points); // universal function to calculate statistics const mitk::ImageStatisticsContainer::Pointer ComputeStatistics(mitk::Image::ConstPointer image, mitk::MaskGenerator::Pointer maskGen = nullptr, mitk::MaskGenerator::Pointer secondardMaskGen = nullptr, unsigned short label = 1); void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian = 0); // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, mitk::ImageStatisticsContainer::VoxelCountType N, mitk::ImageStatisticsContainer::RealType mean, mitk::ImageStatisticsContainer::RealType MPP, mitk::ImageStatisticsContainer::RealType skewness, mitk::ImageStatisticsContainer::RealType kurtosis, mitk::ImageStatisticsContainer::RealType variance, mitk::ImageStatisticsContainer::RealType stdev, mitk::ImageStatisticsContainer::RealType min, mitk::ImageStatisticsContainer::RealType max, mitk::ImageStatisticsContainer::RealType RMS, mitk::ImageStatisticsContainer::IndexType minIndex, mitk::ImageStatisticsContainer::IndexType maxIndex); }; void mitkImageStatisticsCalculatorTestSuite::TestUninitializedImage() { /***************************** * loading uninitialized image to datastorage ******************************/ MITK_INFO << std::endl << "Test uninitialized image: -----------------------------------------------------------------------------------"; mitk::Image::Pointer image = mitk::Image::New(); mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(image); mitk::ImageStatisticsCalculator::Pointer is = mitk::ImageStatisticsCalculator::New(); CPPUNIT_ASSERT_THROW(is->GetStatistics(), mitk::Exception); } void mitkImageStatisticsCalculatorTestSuite::TestCase1() { /***************************** * one whole white pixel * -> mean of 255 expected ******************************/ MITK_INFO << std::endl << "Test case 1:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); CPPUNIT_ASSERT_MESSAGE("Failed loading an mitk::Image", m_TestImage.IsNotNull()); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); CPPUNIT_ASSERT_MESSAGE("Failed getting image geometry", m_Geometry.IsNotNull()); mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5; mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; mitk::Point2D pnt4; pnt4[0] = 10.5; pnt4[1] = 4.5; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::ImageStatisticsContainer::Pointer statisticsContainer; mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase2() { /***************************** * half pixel in x-direction (white) * -> mean of 255 expected ******************************/ MITK_INFO << std::endl << "Test case 2:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 10.0; pnt1[1] = 3.5; mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; mitk::Point2D pnt4; pnt4[0] = 10.0; pnt4[1] = 4.5; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase3() { /***************************** * half pixel in diagonal-direction (white) * -> mean of 255 expected ******************************/ MITK_INFO << std::endl << "Test case 3:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 10.5; pnt1[1] = 3.5; mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; std::vector points{ pnt1,pnt2,pnt3 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 255.0, 0.0, 255.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase4() { /***************************** * one pixel (white) + 2 half pixels (white) + 1 half pixel (black) * -> mean of 191.25 expected ******************************/ MITK_INFO << std::endl << "Test case 4:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 1.1; pnt1[1] = 1.1; mitk::Point2D pnt2; pnt2[0] = 2.0; pnt2[1] = 2.0; mitk::Point2D pnt3; pnt3[0] = 3.0; pnt3[1] = 1.0; mitk::Point2D pnt4; pnt4[0] = 2.0; pnt4[1] = 0.0; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 191.25, 110.41823898251593, 253.72499847412109); } void mitkImageStatisticsCalculatorTestSuite::TestCase5() { /***************************** * whole pixel (white) + half pixel (gray) in x-direction * -> mean of 191.5 expected ******************************/ MITK_INFO << std::endl << "Test case 5:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 3.5; mitk::Point2D pnt3; pnt3[0] = 9.5; pnt3[1] = 4.5; mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 191.50, 63.50, 128.63499999046327); } void mitkImageStatisticsCalculatorTestSuite::TestCase6() { /***************************** * quarter pixel (black) + whole pixel (white) + half pixel (gray) in x-direction * -> mean of 191.5 expected ******************************/ MITK_INFO << std::endl << "Test case 6:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; mitk::Point2D pnt2; pnt2[0] = 9.25; pnt2[1] = 3.5; mitk::Point2D pnt3; pnt3[0] = 9.25; pnt3[1] = 4.5; mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.5; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 63.50, 128.63499999046327); } void mitkImageStatisticsCalculatorTestSuite::TestCase7() { /***************************** * half pixel (black) + whole pixel (white) + half pixel (gray) in x-direction * -> mean of 127.66 expected ******************************/ MITK_INFO << std::endl << "Test case 7:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 11.0; pnt1[1] = 3.5; mitk::Point2D pnt2; pnt2[0] = 9.0; pnt2[1] = 3.5; mitk::Point2D pnt3; pnt3[0] = 9.0; pnt3[1] = 4.0; mitk::Point2D pnt4; pnt4[0] = 11.0; pnt4[1] = 4.0; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 104.10358089689113, 128.7750015258789); } void mitkImageStatisticsCalculatorTestSuite::TestCase8() { /***************************** * whole pixel (gray) * -> mean of 128 expected ******************************/ MITK_INFO << std::endl << "Test case 8:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 11.5; mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 11.5; mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 128.0, 0.0, 128.0); } void mitkImageStatisticsCalculatorTestSuite::TestCase9() { /***************************** * whole pixel (gray) + half pixel (white) in y-direction * -> mean of 191.5 expected ******************************/ MITK_INFO << std::endl << "Test case 9:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 12.0; mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 12.0; mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 191.5, 63.50, 128.63499999046327); } void mitkImageStatisticsCalculatorTestSuite::TestCase10() { /***************************** * 2 whole pixel (white) + 2 whole pixel (black) in y-direction * -> mean of 127.66 expected ******************************/ MITK_INFO << std::endl << "Test case 10:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 11.5; pnt1[1] = 10.5; mitk::Point2D pnt2; pnt2[0] = 11.5; pnt2[1] = 13.5; mitk::Point2D pnt3; pnt3[0] = 12.5; pnt3[1] = 13.5; mitk::Point2D pnt4; pnt4[0] = 12.5; pnt4[1] = 10.5; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 127.666666666666667, 104.10358089689113, 128.7750015258789); } void mitkImageStatisticsCalculatorTestSuite::TestCase11() { /***************************** * 9 whole pixels (white) + 3 half pixels (white) * + 3 whole pixel (black) [ + 3 slightly less than half pixels (black)] * -> mean of 204.0 expected ******************************/ MITK_INFO << std::endl << "Test case 11:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 0.5; pnt1[1] = 0.5; mitk::Point2D pnt2; pnt2[0] = 3.5; pnt2[1] = 3.5; mitk::Point2D pnt3; pnt3[0] = 8.4999; pnt3[1] = 3.5; mitk::Point2D pnt4; pnt4[0] = 5.4999; pnt4[1] = 0.5; std::vector points{ pnt1,pnt2,pnt3,pnt4 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 204.0, 102.00, 253.724998474121083); } void mitkImageStatisticsCalculatorTestSuite::TestCase12() { /***************************** * half pixel (white) + whole pixel (white) + half pixel (black) * -> mean of 212.66 expected ******************************/ MITK_INFO << std::endl << "Test case 12:-----------------------------------------------------------------------------------"; std::string filename = this->GetTestDataFilePath("ImageStatisticsTestData/testimage.nrrd"); m_TestImage = mitk::IOUtil::Load(filename); m_Geometry = m_TestImage->GetSlicedGeometry()->GetPlaneGeometry(0); mitk::Point2D pnt1; pnt1[0] = 9.5; pnt1[1] = 0.5; mitk::Point2D pnt2; pnt2[0] = 9.5; pnt2[1] = 2.5; mitk::Point2D pnt3; pnt3[0] = 11.5; pnt3[1] = 2.5; std::vector points{ pnt1,pnt2,pnt3 }; auto figure = GeneratePlanarPolygon(m_Geometry, points); mitk::PlanarFigureMaskGenerator::Pointer planFigMaskGen = mitk::PlanarFigureMaskGenerator::New(); planFigMaskGen->SetInputImage(m_TestImage); planFigMaskGen->SetPlanarFigure(figure.GetPointer()); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_TestImage, planFigMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); this->VerifyStatistics(statisticsObjectTimestep0, 212.666666666666667, 59.8683741404609923, 254.36499786376954); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedNoMask() { MITK_INFO << std::endl << "Test Pic3D cropped without mask:-----------------------------------------------------------------------------------"; std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); //calculated ground truth via script mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27; mitk::ImageStatisticsContainer::RealType expected_mean = -564.1481481481481481; mitk::ImageStatisticsContainer::RealType expected_MPP = 113.66666666666667; //mitk::ImageStatisticsContainer::RealType expected_median = -825; mitk::ImageStatisticsContainer::RealType expected_skewness = 0.7120461106763573; mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.8794464383714844; mitk::ImageStatisticsContainer::RealType expected_variance = 140541.38545953357; mitk::ImageStatisticsContainer::RealType expected_standarddev = 374.88849736892911; mitk::ImageStatisticsContainer::RealType expected_min = -927; mitk::ImageStatisticsContainer::RealType expected_max = 147; mitk::ImageStatisticsContainer::RealType expected_RMS = 677.35110431630551; mitk::ImageStatisticsContainer::IndexType expected_minIndex; expected_minIndex.set_size(3); expected_minIndex[0] = 2; expected_minIndex[1] = 1; expected_minIndex[2] = 1; mitk::ImageStatisticsContainer::IndexType expected_maxIndex; expected_maxIndex.set_size(3); expected_maxIndex[0] = 0; expected_maxIndex[1] = 1; expected_maxIndex[2] = 2; mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage)); auto statisticsObject = statisticsContainer->GetStatisticsForTimeStep(0); VerifyStatistics(statisticsObject, expected_N, expected_mean, expected_MPP, expected_skewness, expected_kurtosis, expected_variance, expected_standarddev, expected_min, expected_max, expected_RMS, expected_minIndex, expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedBinMask() { MITK_INFO << std::endl << "Test Pic3D cropped binary mask:-----------------------------------------------------------------------------------"; std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); std::string Pic3DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedBinMask.nrrd"); m_Pic3DCroppedBinMask = mitk::IOUtil::Load(Pic3DCroppedBinMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_Pic3DCroppedBinMask.IsNotNull()); //calculated ground truth via script mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0765697398089618; mitk::ImageStatisticsContainer::RealType expected_MPP = -nan(""); mitk::ImageStatisticsContainer::RealType expected_max = -22; mitk::ImageStatisticsContainer::RealType expected_mean = -464; mitk::ImageStatisticsContainer::RealType expected_min = -846; mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4; mitk::ImageStatisticsContainer::RealType expected_RMS = 595.42631785973322; mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0544059290851858; mitk::ImageStatisticsContainer::RealType expected_standarddev = 373.14407405183323; mitk::ImageStatisticsContainer::RealType expected_variance = 139236.50; mitk::ImageStatisticsContainer::IndexType expected_minIndex; expected_minIndex.set_size(3); expected_minIndex[0] = 1; expected_minIndex[1] = 0; expected_minIndex[2] = 0; mitk::ImageStatisticsContainer::IndexType expected_maxIndex; expected_maxIndex.set_size(3); expected_maxIndex[0] = 0; expected_maxIndex[1] = 0; expected_maxIndex[2] = 1; mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New(); imgMaskGen->SetImageMask(m_Pic3DCroppedBinMask); imgMaskGen->SetInputImage(m_Pic3DCroppedImage); imgMaskGen->SetTimeStep(0); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 1)); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); VerifyStatistics(statisticsObjectTimestep0, expected_N, expected_mean, expected_MPP, expected_skewness, expected_kurtosis, expected_variance, expected_standarddev, expected_min, expected_max, expected_RMS, expected_minIndex, expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedMultilabelMask() { MITK_INFO << std::endl << "Test Pic3D cropped multilabel mask:-----------------------------------------------------------------------------------"; std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); std::string Pic3DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedMultilabelMask.nrrd"); m_Pic3DCroppedMultilabelMask = mitk::IOUtil::Load(Pic3DCroppedMultilabelMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D multilabel mask", m_Pic3DCroppedMultilabelMask.IsNotNull()); //calculated ground truth via script mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5; mitk::ImageStatisticsContainer::RealType expected_MPP = -nan(""); mitk::ImageStatisticsContainer::RealType expected_max = -22; mitk::ImageStatisticsContainer::RealType expected_mean = -586.33333333333333; mitk::ImageStatisticsContainer::RealType expected_min = -916; mitk::ImageStatisticsContainer::VoxelCountType expected_N = 3; mitk::ImageStatisticsContainer::RealType expected_RMS = 710.3006405741163; mitk::ImageStatisticsContainer::RealType expected_skewness = 0.6774469597523700; mitk::ImageStatisticsContainer::RealType expected_standarddev = 400.92421007245525; mitk::ImageStatisticsContainer::RealType expected_variance = 160740.22222222222; mitk::ImageStatisticsContainer::IndexType expected_minIndex; expected_minIndex.set_size(3); expected_minIndex[0] = 2; expected_minIndex[1] = 0; expected_minIndex[2] = 1; mitk::ImageStatisticsContainer::IndexType expected_maxIndex; expected_maxIndex.set_size(3); expected_maxIndex[0] = 0; expected_maxIndex[1] = 0; expected_maxIndex[2] = 1; mitk::ImageMaskGenerator::Pointer imgMaskGen = mitk::ImageMaskGenerator::New(); imgMaskGen->SetImageMask(m_Pic3DCroppedMultilabelMask); imgMaskGen->SetInputImage(m_Pic3DCroppedImage); imgMaskGen->SetTimeStep(0); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, imgMaskGen.GetPointer(), nullptr, 2)); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); VerifyStatistics(statisticsObjectTimestep0, expected_N, expected_mean, expected_MPP, expected_skewness, expected_kurtosis, expected_variance, expected_standarddev, expected_min, expected_max, expected_RMS, expected_minIndex, expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestPic3DCroppedPlanarFigure() { MITK_INFO << std::endl << "Test Pic3D cropped planar figure:-----------------------------------------------------------------------------------"; std::string Pic3DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_cropped.nrrd"); m_Pic3DCroppedImage = mitk::IOUtil::Load(Pic3DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D_cropped", m_Pic3DCroppedImage.IsNotNull()); std::string Pic3DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/Pic3D_croppedPF.pf"); m_Pic3DCroppedPlanarFigure = mitk::IOUtil::Load(Pic3DCroppedPlanarFigureFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D planar figure", m_Pic3DCroppedPlanarFigure.IsNotNull()); //calculated ground truth via script mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1; mitk::ImageStatisticsContainer::RealType expected_MPP = -nan(""); mitk::ImageStatisticsContainer::RealType expected_max = -67; mitk::ImageStatisticsContainer::RealType expected_mean = -446; mitk::ImageStatisticsContainer::RealType expected_min = -825; mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2; mitk::ImageStatisticsContainer::RealType expected_RMS = 585.28369189650243; mitk::ImageStatisticsContainer::RealType expected_skewness = 0; mitk::ImageStatisticsContainer::RealType expected_standarddev = 379; mitk::ImageStatisticsContainer::RealType expected_variance = 143641; mitk::ImageStatisticsContainer::IndexType expected_minIndex; expected_minIndex.set_size(3); expected_minIndex[0] = 1; expected_minIndex[1] = 1; expected_minIndex[2] = 1; mitk::ImageStatisticsContainer::IndexType expected_maxIndex; expected_maxIndex.set_size(3); expected_maxIndex[0] = 0; expected_maxIndex[1] = 1; expected_maxIndex[2] = 1; mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New(); pfMaskGen->SetInputImage(m_Pic3DCroppedImage); pfMaskGen->SetPlanarFigure(m_Pic3DCroppedPlanarFigure); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_Pic3DCroppedImage, pfMaskGen.GetPointer())); auto statisticsObjectTimestep0 = statisticsContainer->GetStatisticsForTimeStep(0); VerifyStatistics(statisticsObjectTimestep0, expected_N, expected_mean, expected_MPP, expected_skewness, expected_kurtosis, expected_variance, expected_standarddev, expected_min, expected_max, expected_RMS, expected_minIndex, expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedNoMaskTimeStep1() { MITK_INFO << std::endl << "Test US4D cropped without mask timestep 1:-----------------------------------------------------------------------------------"; std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); //calculated ground truth via script mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5398359155908228; mitk::ImageStatisticsContainer::RealType expected_MPP = 157.74074074074073; mitk::ImageStatisticsContainer::RealType expected_max = 199; mitk::ImageStatisticsContainer::RealType expected_mean = 157.74074074074073; mitk::ImageStatisticsContainer::RealType expected_min = 101; mitk::ImageStatisticsContainer::VoxelCountType expected_N = 27; mitk::ImageStatisticsContainer::RealType expected_RMS = 160.991718213494823; mitk::ImageStatisticsContainer::RealType expected_skewness = 0.0347280313508018; mitk::ImageStatisticsContainer::RealType expected_standarddev = 32.189936997387058; mitk::ImageStatisticsContainer::RealType expected_variance = 1036.19204389574722; mitk::ImageStatisticsContainer::IndexType expected_minIndex; expected_minIndex.set_size(3); expected_minIndex[0] = 0; expected_minIndex[1] = 2; expected_minIndex[2] = 0; mitk::ImageStatisticsContainer::IndexType expected_maxIndex; expected_maxIndex.set_size(3); expected_maxIndex[0] = 0; expected_maxIndex[1] = 0; expected_maxIndex[2] = 1; mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New(); CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage)); auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); VerifyStatistics(statisticsObjectTimestep1, expected_N, expected_mean, expected_MPP, expected_skewness, expected_kurtosis, expected_variance, expected_standarddev, expected_min, expected_max, expected_RMS, expected_minIndex, expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedBinMaskTimeStep1() { MITK_INFO << std::endl << "Test US4D cropped with binary mask timestep 1:-----------------------------------------------------------------------------------"; std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); std::string US4DCroppedBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedBinMask.nrrd"); m_US4DCroppedBinMask = mitk::IOUtil::Load(US4DCroppedBinMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D binary mask", m_US4DCroppedBinMask.IsNotNull()); //calculated ground truth via script mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.5863739712889191; mitk::ImageStatisticsContainer::RealType expected_MPP = 166.75; mitk::ImageStatisticsContainer::RealType expected_max = 199; mitk::ImageStatisticsContainer::RealType expected_mean = 166.75; mitk::ImageStatisticsContainer::RealType expected_min = 120; mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4; mitk::ImageStatisticsContainer::RealType expected_RMS = 169.70636405273669; mitk::ImageStatisticsContainer::RealType expected_skewness = -0.4285540263894276; mitk::ImageStatisticsContainer::RealType expected_standarddev = 31.538666744172936; mitk::ImageStatisticsContainer::RealType expected_variance = 994.6874999999999; mitk::ImageStatisticsContainer::IndexType expected_minIndex; expected_minIndex.set_size(3); expected_minIndex[0] = 0; expected_minIndex[1] = 0; expected_minIndex[2] = 2; mitk::ImageStatisticsContainer::IndexType expected_maxIndex; expected_maxIndex.set_size(3); expected_maxIndex[0] = 1; expected_maxIndex[1] = 1; expected_maxIndex[2] = 1; mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); imgMask1->SetInputImage(m_US4DCroppedImage); imgMask1->SetImageMask(m_US4DCroppedBinMask); mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New(); CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1)); auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); VerifyStatistics(statisticsObjectTimestep1, expected_N, expected_mean, expected_MPP, expected_skewness, expected_kurtosis, expected_variance, expected_standarddev, expected_min, expected_max, expected_RMS, expected_minIndex, expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedMultilabelMaskTimeStep1() { MITK_INFO << std::endl << "Test US4D cropped with mulitlabel mask timestep 1:-----------------------------------------------------------------------------------"; std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); std::string US4DCroppedMultilabelMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedMultilabelMask.nrrd"); m_US4DCroppedMultilabelMask = mitk::IOUtil::Load(US4DCroppedMultilabelMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D multilabel mask", m_US4DCroppedMultilabelMask.IsNotNull()); //calculated ground truth via script mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1.0432484564918287; mitk::ImageStatisticsContainer::RealType expected_MPP = 159.75; mitk::ImageStatisticsContainer::RealType expected_max = 199; mitk::ImageStatisticsContainer::RealType expected_mean = 159.75; mitk::ImageStatisticsContainer::RealType expected_min = 120; mitk::ImageStatisticsContainer::VoxelCountType expected_N = 4; mitk::ImageStatisticsContainer::RealType expected_RMS = 163.74446555532802; mitk::ImageStatisticsContainer::RealType expected_skewness = -0.004329226115093; mitk::ImageStatisticsContainer::RealType expected_standarddev = 35.947009611371016; mitk::ImageStatisticsContainer::RealType expected_variance = 1292.187500000000227; mitk::ImageStatisticsContainer::IndexType expected_minIndex; expected_minIndex.set_size(3); expected_minIndex[0] = 0; expected_minIndex[1] = 0; expected_minIndex[2] = 2; mitk::ImageStatisticsContainer::IndexType expected_maxIndex; expected_maxIndex.set_size(3); expected_maxIndex[0] = 0; expected_maxIndex[1] = 0; expected_maxIndex[2] = 1; mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); imgMask1->SetInputImage(m_US4DCroppedImage); imgMask1->SetImageMask(m_US4DCroppedMultilabelMask); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1)); auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); VerifyStatistics(statisticsObjectTimestep1, expected_N, expected_mean, expected_MPP, expected_skewness, expected_kurtosis, expected_variance, expected_standarddev, expected_min, expected_max, expected_RMS, expected_minIndex, expected_maxIndex); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedPlanarFigureTimeStep1() { MITK_INFO << std::endl << "Test US4D cropped planar figure timestep 1:-----------------------------------------------------------------------------------"; std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); std::string US4DCroppedPlanarFigureFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_croppedPF.pf"); m_US4DCroppedPlanarFigure = mitk::IOUtil::Load(US4DCroppedPlanarFigureFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D planar figure", m_US4DCroppedPlanarFigure.IsNotNull()); //calculated ground truth via script mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1; mitk::ImageStatisticsContainer::RealType expected_MPP = 172.5; mitk::ImageStatisticsContainer::RealType expected_max = 197; mitk::ImageStatisticsContainer::RealType expected_mean = 172.5; mitk::ImageStatisticsContainer::RealType expected_min = 148; mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2; mitk::ImageStatisticsContainer::RealType expected_RMS = 174.23116827938679; mitk::ImageStatisticsContainer::RealType expected_skewness = 0; mitk::ImageStatisticsContainer::RealType expected_standarddev = 24.5; mitk::ImageStatisticsContainer::RealType expected_variance = 600.25; mitk::ImageStatisticsContainer::IndexType expected_minIndex; expected_minIndex.set_size(3); expected_minIndex[0] = 2; expected_minIndex[1] = 2; expected_minIndex[2] = 2; mitk::ImageStatisticsContainer::IndexType expected_maxIndex; expected_maxIndex.set_size(3); expected_maxIndex[0] = 2; expected_maxIndex[1] = 2; expected_maxIndex[2] = 1; mitk::PlanarFigureMaskGenerator::Pointer pfMaskGen = mitk::PlanarFigureMaskGenerator::New(); pfMaskGen->SetInputImage(m_US4DCroppedImage); pfMaskGen->SetPlanarFigure(m_US4DCroppedPlanarFigure); mitk::ImageStatisticsContainer::Pointer statisticsContainer; CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, pfMaskGen.GetPointer())); auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); VerifyStatistics(statisticsObjectTimestep1, expected_N, expected_mean, expected_MPP, expected_skewness, expected_kurtosis, expected_variance, expected_standarddev, expected_min, expected_max, expected_RMS, expected_minIndex, expected_maxIndex); } void mitkImageStatisticsCalculatorTestSuite::TestUS4DCroppedAllTimesteps() { MITK_INFO << std::endl << "Test US4D cropped all timesteps:-----------------------------------------------------------------------------------"; std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); mitk::ImageStatisticsContainer::Pointer statisticsContainer=mitk::ImageStatisticsContainer::New(); CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage)); for (int i = 0; i < 4; i++) { CPPUNIT_ASSERT_MESSAGE("Error computing statistics for multiple timestep", statisticsContainer->TimeStepExists(i)); } } void mitkImageStatisticsCalculatorTestSuite::TestUS4DCropped3DMask() { MITK_INFO << std::endl << "Test US4D cropped with 3D binary Mask:-----------------------------------------------------------------------------------"; std::string US4DCroppedFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped.nrrd"); m_US4DCroppedImage = mitk::IOUtil::Load(US4DCroppedFile); CPPUNIT_ASSERT_MESSAGE("Failed loading US4D_cropped", m_US4DCroppedImage.IsNotNull()); std::string US4DCropped3DBinMaskFile = this->GetTestDataFilePath("ImageStatisticsTestData/US4D_cropped3DBinMask.nrrd"); m_US4DCropped3DBinMask = mitk::IOUtil::Load(US4DCropped3DBinMaskFile); CPPUNIT_ASSERT_MESSAGE("Failed loading Pic3D binary mask", m_US4DCropped3DBinMask.IsNotNull()); //calculated ground truth via script mitk::ImageStatisticsContainer::RealType expected_kurtosis = 1; mitk::ImageStatisticsContainer::RealType expected_MPP = 198; mitk::ImageStatisticsContainer::RealType expected_max = 199; mitk::ImageStatisticsContainer::RealType expected_mean = 198; mitk::ImageStatisticsContainer::RealType expected_min = 197; mitk::ImageStatisticsContainer::VoxelCountType expected_N = 2; mitk::ImageStatisticsContainer::RealType expected_RMS = 198.00252523642217; mitk::ImageStatisticsContainer::RealType expected_skewness = 0; mitk::ImageStatisticsContainer::RealType expected_standarddev = 1; mitk::ImageStatisticsContainer::RealType expected_variance = 1; mitk::ImageStatisticsContainer::IndexType expected_minIndex; expected_minIndex.set_size(3); expected_minIndex[0] = 1; expected_minIndex[1] = 2; expected_minIndex[2] = 1; mitk::ImageStatisticsContainer::IndexType expected_maxIndex; expected_maxIndex.set_size(3); expected_maxIndex[0] = 1; expected_maxIndex[1] = 1; expected_maxIndex[2] = 1; mitk::ImageMaskGenerator::Pointer imgMask1 = mitk::ImageMaskGenerator::New(); imgMask1->SetInputImage(m_US4DCroppedImage); imgMask1->SetImageMask(m_US4DCropped3DBinMask); mitk::ImageStatisticsContainer::Pointer statisticsContainer = mitk::ImageStatisticsContainer::New(); CPPUNIT_ASSERT_NO_THROW(statisticsContainer = ComputeStatistics(m_US4DCroppedImage, imgMask1.GetPointer(), nullptr, 1)); auto statisticsObjectTimestep1 = statisticsContainer->GetStatisticsForTimeStep(1); VerifyStatistics(statisticsObjectTimestep1, expected_N, expected_mean, expected_MPP, expected_skewness, expected_kurtosis, expected_variance, expected_standarddev, expected_min, expected_max, expected_RMS, expected_minIndex, expected_maxIndex); } mitk::PlanarPolygon::Pointer mitkImageStatisticsCalculatorTestSuite::GeneratePlanarPolygon(mitk::PlaneGeometry::Pointer geometry, std::vector points) { mitk::PlanarPolygon::Pointer figure = mitk::PlanarPolygon::New(); figure->SetPlaneGeometry(geometry); figure->PlaceFigure(points[0]); for (unsigned int i = 1; i < points.size(); i++) { figure->SetControlPoint(i, points[i], true); } return figure; } const mitk::ImageStatisticsContainer::Pointer mitkImageStatisticsCalculatorTestSuite::ComputeStatistics(mitk::Image::ConstPointer image, mitk::MaskGenerator::Pointer maskGen, mitk::MaskGenerator::Pointer secondardMaskGen, unsigned short label) { mitk::ImageStatisticsCalculator::Pointer imgStatCalc = mitk::ImageStatisticsCalculator::New(); imgStatCalc->SetInputImage(image); if (maskGen.IsNotNull()) { imgStatCalc->SetMask(maskGen.GetPointer()); if (secondardMaskGen.IsNotNull()) { imgStatCalc->SetSecondaryMask(secondardMaskGen.GetPointer()); } } return imgStatCalc->GetStatistics(label); } void mitkImageStatisticsCalculatorTestSuite::VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, mitk::ImageStatisticsContainer::RealType testMean, mitk::ImageStatisticsContainer::RealType testSD, mitk::ImageStatisticsContainer::RealType testMedian) { mitk::ImageStatisticsContainer::RealType meanObject = 0; mitk::ImageStatisticsContainer::RealType standardDeviationObject = 0; mitk::ImageStatisticsContainer::RealType medianObject = 0; CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEAN())); CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::STANDARDDEVIATION())); CPPUNIT_ASSERT_NO_THROW(medianObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEDIAN())); CPPUNIT_ASSERT_MESSAGE("Calculated mean grayvalue is not equal to the desired value.", std::abs(meanObject - testMean) < mitk::eps); CPPUNIT_ASSERT_MESSAGE("Calculated grayvalue sd is not equal to the desired value.", std::abs(standardDeviationObject - testSD) < mitk::eps); CPPUNIT_ASSERT_MESSAGE("Calculated median grayvalue is not equal to the desired value.", std::abs(medianObject - testMedian) < mitk::eps); } // T26098 histogram statistics need to be tested (median, uniformity, UPP, entropy) void mitkImageStatisticsCalculatorTestSuite::VerifyStatistics(mitk::ImageStatisticsContainer::ImageStatisticsObject stats, mitk::ImageStatisticsContainer::VoxelCountType N, mitk::ImageStatisticsContainer::RealType mean, mitk::ImageStatisticsContainer::RealType MPP, mitk::ImageStatisticsContainer::RealType skewness, mitk::ImageStatisticsContainer::RealType kurtosis, mitk::ImageStatisticsContainer::RealType variance, mitk::ImageStatisticsContainer::RealType stdev, mitk::ImageStatisticsContainer::RealType min, mitk::ImageStatisticsContainer::RealType max, mitk::ImageStatisticsContainer::RealType RMS, mitk::ImageStatisticsContainer::IndexType minIndex, mitk::ImageStatisticsContainer::IndexType maxIndex) { mitk::ImageStatisticsContainer::VoxelCountType numberOfVoxelsObject; mitk::ImageStatisticsContainer::RealType meanObject = 0; mitk::ImageStatisticsContainer::RealType mppObject = 0; mitk::ImageStatisticsContainer::RealType skewnessObject = 0; mitk::ImageStatisticsContainer::RealType kurtosisObject = 0; mitk::ImageStatisticsContainer::RealType varianceObject = 0; mitk::ImageStatisticsContainer::RealType standardDeviationObject = 0; mitk::ImageStatisticsContainer::RealType minObject = 0; mitk::ImageStatisticsContainer::RealType maxObject = 0; mitk::ImageStatisticsContainer::RealType rmsObject = 0; - mitk::ImageStatisticsContainer::IndexType minIndexObject = {0, 0, 0}; - mitk::ImageStatisticsContainer::IndexType maxIndexObject = {0, 0, 0}; + mitk::ImageStatisticsContainer::IndexType minIndexObject(3,0); + mitk::ImageStatisticsContainer::IndexType maxIndexObject(3,0); CPPUNIT_ASSERT_NO_THROW(numberOfVoxelsObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::NUMBEROFVOXELS())); CPPUNIT_ASSERT_NO_THROW(meanObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MEAN())); CPPUNIT_ASSERT_NO_THROW(mppObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MPP())); CPPUNIT_ASSERT_NO_THROW(skewnessObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::SKEWNESS())); CPPUNIT_ASSERT_NO_THROW(kurtosisObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::KURTOSIS())); CPPUNIT_ASSERT_NO_THROW(varianceObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::VARIANCE())); CPPUNIT_ASSERT_NO_THROW(standardDeviationObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::STANDARDDEVIATION())); CPPUNIT_ASSERT_NO_THROW(minObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MINIMUM())); CPPUNIT_ASSERT_NO_THROW(maxObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MAXIMUM())); CPPUNIT_ASSERT_NO_THROW(rmsObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::RMS())); CPPUNIT_ASSERT_NO_THROW(minIndexObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MINIMUMPOSITION())); CPPUNIT_ASSERT_NO_THROW(maxIndexObject = stats.GetValueConverted(mitk::ImageStatisticsConstants::MAXIMUMPOSITION())); CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", numberOfVoxelsObject - N == 0); CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(meanObject - mean) < mitk::eps); // in three test cases MPP is None because the roi has no positive pixels if (!std::isnan(mppObject)) { CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(mppObject - MPP) < mitk::eps); } CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(skewnessObject - skewness) < mitk::eps); CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(kurtosisObject - kurtosis) < mitk::eps); CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(varianceObject - variance) < mitk::eps); CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(standardDeviationObject - stdev) < mitk::eps); CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minObject - min) < mitk::eps); CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxObject - max) < mitk::eps); CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(rmsObject - RMS) < mitk::eps); for (unsigned int i = 0; i < minIndex.size(); ++i) { CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(minIndexObject[i] - minIndex[i]) < mitk::eps); } for (unsigned int i = 0; i < maxIndex.size(); ++i) { CPPUNIT_ASSERT_MESSAGE("Calculated value does not fit expected value", std::abs(maxIndexObject[i] - maxIndex[i]) < mitk::eps); } } MITK_TEST_SUITE_REGISTRATION(mitkImageStatisticsCalculator) diff --git a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp b/Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.cpp similarity index 90% rename from Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp rename to Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.cpp index cee321423e..0b11df2dad 100644 --- a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.cpp +++ b/Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.cpp @@ -1,410 +1,407 @@ /*============================================================================ 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 "mitkAlgorithmHelper.h" +#include "mitkMAPAlgorithmHelper.h" //itk #include // Mitk #include #include // MatchPoint #include #include #include #include #include namespace mitk { - MITKAlgorithmHelper::MITKAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase *algorithm) + MAPAlgorithmHelper::MAPAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase *algorithm) : m_AlgorithmBase(algorithm), m_Error(CheckError::none) { m_AllowImageCasting = true; } - bool MITKAlgorithmHelper::HasImageAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm) + bool MAPAlgorithmHelper::HasImageAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm) { using InternalDefault2DImageType = itk::Image; using InternalDefault3DImageType = itk::Image; using Alg2DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface; if (dynamic_cast(algorithm) != nullptr) return true; using Alg3DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface; if (dynamic_cast(algorithm) != nullptr) return true; using Alg2D3DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface; if (dynamic_cast(algorithm) != nullptr) return true; using Alg3D2DType = const ::map::algorithm::facet::ImageRegistrationAlgorithmInterface; if (dynamic_cast(algorithm) != nullptr) return true; return false; - }; + } - bool MITKAlgorithmHelper::HasPointSetAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm) + bool MAPAlgorithmHelper::HasPointSetAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm) { typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType; typedef const ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface PointSetRegInterface; return dynamic_cast(algorithm) != nullptr; - }; + } map::core::RegistrationBase::Pointer - MITKAlgorithmHelper:: + MAPAlgorithmHelper:: GetRegistration() const { map::core::RegistrationBase::Pointer spResult; unsigned int movingDim = m_AlgorithmBase->getMovingDimensions(); unsigned int targetDim = m_AlgorithmBase->getTargetDimensions(); if (movingDim != targetDim) { mapDefaultExceptionStaticMacro( << - "Error, algorithm instance has unequal dimensionality and is therefore not supported in the current version of MITKAlgorithmHelper."); + "Error, algorithm instance has unequal dimensionality and is therefore not supported in the current version of MAPAlgorithmHelper."); } if (movingDim > 3) { mapDefaultExceptionStaticMacro( << - "Error, algorithm instance has a dimensionality larger than 3 and is therefore not supported in the current version of MITKAlgorithmHelper."); + "Error, algorithm instance has a dimensionality larger than 3 and is therefore not supported in the current version of MAPAlgorithmHelper."); } typedef ::map::algorithm::facet::RegistrationAlgorithmInterface<2, 2> RegistrationAlg2D2DInterface; typedef ::map::algorithm::facet::RegistrationAlgorithmInterface<3, 3> RegistrationAlg3D3DInterface; RegistrationAlg2D2DInterface* pRegAlgorithm2D2D = dynamic_cast (m_AlgorithmBase.GetPointer()); RegistrationAlg3D3DInterface* pRegAlgorithm3D3D = dynamic_cast (m_AlgorithmBase.GetPointer()); if (pRegAlgorithm2D2D) { spResult = pRegAlgorithm2D2D->getRegistration(); } if (pRegAlgorithm3D3D) { spResult = pRegAlgorithm3D3D->getRegistration(); } return spResult; } mitk::MAPRegistrationWrapper::Pointer - MITKAlgorithmHelper:: + MAPAlgorithmHelper:: GetMITKRegistrationWrapper() const { map::core::RegistrationBase::Pointer spInternalResult = GetRegistration(); - mitk::MAPRegistrationWrapper::Pointer spResult = mitk::MAPRegistrationWrapper::New(); - spResult->SetRegistration(spInternalResult); + mitk::MAPRegistrationWrapper::Pointer spResult = mitk::MAPRegistrationWrapper::New(spInternalResult); return spResult; - }; + } static const mitk::Image* GetDataAsImage(const mitk::BaseData* data) { return dynamic_cast(data); - }; + } static const mitk::PointSet* GetDataAsPointSet(const mitk::BaseData* data) { return dynamic_cast(data); - }; + } bool - MITKAlgorithmHelper:: + MAPAlgorithmHelper:: CheckData(const mitk::BaseData* moving, const mitk::BaseData* target, CheckError::Type& error) const { if (! m_AlgorithmBase) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Helper has no algorithm defined."); } if (! moving) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Moving data pointer is nullptr."); } if (! target) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Target data pointer is nullptr."); } bool result = false; m_Error = CheckError::unsupportedDataType; unsigned int movingDim = m_AlgorithmBase->getMovingDimensions(); unsigned int targetDim = m_AlgorithmBase->getTargetDimensions(); if (movingDim != targetDim) { m_Error = CheckError::wrongDimension; } else { //First check if data are point sets or images if (GetDataAsPointSet(target) && GetDataAsPointSet(moving)) { typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType; typedef ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface PointSetRegInterface; PointSetRegInterface* pPointSetInterface = dynamic_cast (m_AlgorithmBase.GetPointer()); if (!pPointSetInterface) { result = false; m_Error = CheckError::unsupportedDataType; } } else if (GetDataAsImage(moving) && GetDataAsImage(target)) { if (movingDim == 2) { AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoCheckImages, 2); } else if (movingDim == 3) { AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoCheckImages, 3); } else { m_Error = CheckError::wrongDimension; } if (m_Error == CheckError::none || (m_AllowImageCasting && m_Error == CheckError::onlyByCasting)) { result = true; } } } error = m_Error; return result; + } - }; - - void MITKAlgorithmHelper::SetAllowImageCasting(bool allowCasting) + void MAPAlgorithmHelper::SetAllowImageCasting(bool allowCasting) { this->m_AllowImageCasting = allowCasting; - }; + } - bool MITKAlgorithmHelper::GetAllowImageCasting() const + bool MAPAlgorithmHelper::GetAllowImageCasting() const { return this->m_AllowImageCasting; - }; + } - void MITKAlgorithmHelper::SetData(const mitk::BaseData* moving, const mitk::BaseData* target) + void MAPAlgorithmHelper::SetData(const mitk::BaseData* moving, const mitk::BaseData* target) { if (! m_AlgorithmBase) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Helper has no algorithm defined."); } if (! moving) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Moving data pointer is nullptr."); } if (! target) { mapDefaultExceptionStaticMacro( << "Error, cannot check data. Target data pointer is nullptr."); } unsigned int movingDim = m_AlgorithmBase->getMovingDimensions(); unsigned int targetDim = m_AlgorithmBase->getTargetDimensions(); if (movingDim != targetDim) { mapDefaultExceptionStaticMacro( << - "Error, cannot set data. Current version of MITKAlgorithmHelper only supports images/point sets with same dimensionality."); + "Error, cannot set data. Current version of MAPAlgorithmHelper only supports images/point sets with same dimensionality."); } if (GetDataAsPointSet(target) && GetDataAsPointSet(moving)) { typedef ::map::core::continuous::Elements<3>::InternalPointSetType InternalDefaultPointSetType; typedef ::map::algorithm::facet::PointSetRegistrationAlgorithmInterface PointSetRegInterface; PointSetRegInterface* pPointSetInterface = dynamic_cast (m_AlgorithmBase.GetPointer()); pPointSetInterface->setMovingPointSet(mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP( GetDataAsPointSet(moving)->GetPointSet())); pPointSetInterface->setTargetPointSet(mitk::PointSetMappingHelper::ConvertPointSetMITKtoMAP( GetDataAsPointSet(target)->GetPointSet())); } else if (GetDataAsImage(moving) && GetDataAsImage(target)) { if (movingDim == 2) { AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoSetImages, 2); } else if (movingDim == 3) { AccessTwoImagesFixedDimensionByItk(GetDataAsImage(moving), GetDataAsImage(target), DoSetImages, 3); } } - }; + } template - typename TOutImageType::Pointer MITKAlgorithmHelper::CastImage(const TInImageType* input) const + typename TOutImageType::Pointer MAPAlgorithmHelper::CastImage(const TInImageType* input) const { typedef itk::CastImageFilter< TInImageType, TOutImageType > CastFilterType; typename CastFilterType::Pointer spImageCaster = CastFilterType::New(); spImageCaster->SetInput(input); typename TOutImageType::Pointer spImage = spImageCaster->GetOutput(); spImageCaster->Update(); return spImage; } template - void MITKAlgorithmHelper::DoSetImages(const itk::Image* moving, + void MAPAlgorithmHelper::DoSetImages(const itk::Image* moving, const itk::Image* target) { typedef itk::Image MovingImageType; typedef itk::Image TargetImageType; typedef itk::Image InternalDefaultMovingImageType; typedef itk::Image InternalDefaultTargetImageType; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface ImageRegInterface; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface DefaultImageRegInterface; ImageRegInterface* pImageInterface = dynamic_cast(m_AlgorithmBase.GetPointer()); DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast (m_AlgorithmBase.GetPointer()); if (pImageInterface) { //just set directly and you are done /**@todo the duplication work arround is needed due to a insufficuence in the AccessTwoImagesFixedDimensionByItk macro. The macro always cast the passed image into non const (even if tha image was passed as const). This behavior enforces the unnecessary use of an writeaccessor, which as a consequence will lead to redundant access exceptions as long as the algorithm exists; e.g. in the typical scenario with the MatchPoint Plugins*/ typedef itk::ImageDuplicator< MovingImageType > MovingDuplicatorType; typedef itk::ImageDuplicator< TargetImageType > TargetDuplicatorType; typename MovingDuplicatorType::Pointer mDuplicator = MovingDuplicatorType::New(); mDuplicator->SetInputImage(moving); mDuplicator->Update(); typename TargetDuplicatorType::Pointer tDuplicator = TargetDuplicatorType::New(); tDuplicator->SetInputImage(target); tDuplicator->Update(); typename MovingImageType::Pointer clonedMoving = mDuplicator->GetOutput(); typename TargetImageType::Pointer clonedTarget = tDuplicator->GetOutput(); pImageInterface->setTargetImage(clonedTarget); pImageInterface->setMovingImage(clonedMoving); } else if (pDefaultImageInterface) { //you may convert it to the default image type and use it then if (! m_AllowImageCasting) { mapDefaultExceptionStaticMacro( << - "Error, cannot set images. MITKAlgorithmHelper has to convert them into MatchPoint default images, but is not allowed. Please reconfigure helper."); + "Error, cannot set images. MAPAlgorithmHelper has to convert them into MatchPoint default images, but is not allowed. Please reconfigure helper."); } typename InternalDefaultTargetImageType::Pointer spCastedTarget = CastImage(target); typename InternalDefaultMovingImageType::Pointer spCastedMoving = CastImage(moving); pDefaultImageInterface->setTargetImage(spCastedTarget); pDefaultImageInterface->setMovingImage(spCastedMoving); } else { mapDefaultExceptionStaticMacro( << "Error, algorithm is not able to use the based images."); } } template - void MITKAlgorithmHelper::DoCheckImages(const itk::Image* /*moving*/, + void MAPAlgorithmHelper::DoCheckImages(const itk::Image* /*moving*/, const itk::Image* /*target*/) const { typedef itk::Image MovingImageType; typedef itk::Image TargetImageType; typedef itk::Image InternalDefaultMovingImageType; typedef itk::Image InternalDefaultTargetImageType; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface ImageRegInterface; typedef ::map::algorithm::facet::ImageRegistrationAlgorithmInterface DefaultImageRegInterface; ImageRegInterface* pImageInterface = dynamic_cast(m_AlgorithmBase.GetPointer()); DefaultImageRegInterface* pDefaultImageInterface = dynamic_cast (m_AlgorithmBase.GetPointer()); if (pImageInterface) { //just set directly and you are done m_Error = CheckError::none; } else if (pDefaultImageInterface) { //you may convert it to the default image type and use it then m_Error = CheckError::onlyByCasting; } else { m_Error = CheckError::unsupportedDataType; } } mapGenerateAlgorithmUIDPolicyMacro(DummyRegIDPolicy, "de.dkfz.dipp", "Identity", "1.0.0", ""); mitk::MAPRegistrationWrapper::Pointer GenerateIdentityRegistration3D() { typedef map::algorithm::DummyImageRegistrationAlgorithm::InternalImageType, map::core::discrete::Elements<3>::InternalImageType, DummyRegIDPolicy> DummyRegType; DummyRegType::Pointer regAlg = DummyRegType::New(); - mitk::MITKAlgorithmHelper helper(regAlg); + mitk::MAPAlgorithmHelper helper(regAlg); map::core::discrete::Elements<3>::InternalImageType::Pointer dummyImg = map::core::discrete::Elements<3>::InternalImageType::New(); dummyImg->Allocate(); regAlg->setTargetImage(dummyImg); regAlg->setMovingImage(dummyImg); - mitk::MAPRegistrationWrapper::Pointer dummyReg = mitk::MAPRegistrationWrapper::New(); - dummyReg->SetRegistration(regAlg->getRegistration()); + auto dummyReg = mitk::MAPRegistrationWrapper::New(regAlg->getRegistration()); return dummyReg; } } diff --git a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.h b/Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.h similarity index 89% rename from Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.h rename to Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.h index 087c0b2425..70c42632f3 100644 --- a/Modules/MatchPointRegistration/Helper/mitkAlgorithmHelper.h +++ b/Modules/MatchPointRegistration/Helper/mitkMAPAlgorithmHelper.h @@ -1,107 +1,107 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ -#ifndef mitkAlgorithmHelper_h -#define mitkAlgorithmHelper_h +#ifndef mitkMAPAlgorithmHelper_h +#define mitkMAPAlgorithmHelper_h //MatchPoint #include "mapRegistrationAlgorithmBase.h" #include "mapRegistrationBase.h" //MITK #include #include //MITK #include "MitkMatchPointRegistrationExports.h" #include "mitkMAPRegistrationWrapper.h" namespace mitk { /*! - \brief MITKAlgorithmHelper + \brief MAPAlgorithmHelper \remark Current implementation is not thread-save. Just use one Helper class per registration task. \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. */ - class MITKMATCHPOINTREGISTRATION_EXPORT MITKAlgorithmHelper + class MITKMATCHPOINTREGISTRATION_EXPORT MAPAlgorithmHelper { public: - MITKAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase* algorithm); + MAPAlgorithmHelper(map::algorithm::RegistrationAlgorithmBase* algorithm); void SetData(const mitk::BaseData* moving, const mitk::BaseData* target); void SetAllowImageCasting(bool allowCasting); bool GetAllowImageCasting() const; static bool HasImageAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm); static bool HasPointSetAlgorithmInterface(const map::algorithm::RegistrationAlgorithmBase* algorithm); struct CheckError { enum Type { none = 0, onlyByCasting = 1, wrongDimension = 2, unsupportedDataType = 3 }; }; bool CheckData(const mitk::BaseData* moving, const mitk::BaseData* target, CheckError::Type& error) const; map::core::RegistrationBase::Pointer GetRegistration() const; mitk::MAPRegistrationWrapper::Pointer GetMITKRegistrationWrapper() const; - ~MITKAlgorithmHelper() {} + ~MAPAlgorithmHelper() {} private: - MITKAlgorithmHelper& operator = (const MITKAlgorithmHelper&); - MITKAlgorithmHelper(const MITKAlgorithmHelper&); + MAPAlgorithmHelper& operator = (const MAPAlgorithmHelper&); + MAPAlgorithmHelper(const MAPAlgorithmHelper&); /**Internal helper that casts itk images from one pixel type into an other (used by DoSetImages if the images have the right dimension but wrong type and AllowImageCasting is activated)*/ template typename TOutImageType::Pointer CastImage(const TInImageType* input) const; /**Internal helper that is used by SetData if the data are images to set them properly.*/ template void DoSetImages(const itk::Image* moving, const itk::Image* target); /**Internal helper that is used by SetData if the data are images to check if the image types are supported by the algorithm.*/ template void DoCheckImages(const itk::Image* moving, const itk::Image* target) const; map::algorithm::RegistrationAlgorithmBase::Pointer m_AlgorithmBase; bool m_AllowImageCasting; mutable CheckError::Type m_Error; }; /**Small helper function that generates Identity transforms in 3D.*/ mitk::MAPRegistrationWrapper::Pointer MITKMATCHPOINTREGISTRATION_EXPORT GenerateIdentityRegistration3D(); } #endif diff --git a/Modules/MatchPointRegistration/Helper/mitkRegistrationHelper.cpp b/Modules/MatchPointRegistration/Helper/mitkRegistrationHelper.cpp index e4af761b67..b9c95a61ad 100644 --- a/Modules/MatchPointRegistration/Helper/mitkRegistrationHelper.cpp +++ b/Modules/MatchPointRegistration/Helper/mitkRegistrationHelper.cpp @@ -1,149 +1,165 @@ /*============================================================================ 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 "mitkRegistrationHelper.h" #include +#include +#include +#include #include //MatchPoint #include "mapRegistrationKernel.h" namespace mitk { - mitk::NodePredicateDataType::ConstPointer InternalRegNodePredicate = mitk::NodePredicateDataType::New(mitk::MAPRegistrationWrapper::GetStaticNameOfClass()).GetPointer(); - mitk::NodePredicateDataType::ConstPointer InternalImageNodePredicate = mitk::NodePredicateDataType::New(mitk::Image::GetStaticNameOfClass()).GetPointer(); - mitk::NodePredicateDataType::ConstPointer InternalPointSetNodePredicate = mitk::NodePredicateDataType::New(mitk::PointSet::GetStaticNameOfClass()).GetPointer(); + mitk::TNodePredicateDataType::ConstPointer InternalRegNodePredicate = mitk::TNodePredicateDataType::New().GetPointer(); + mitk::TNodePredicateDataType::ConstPointer InternalImageNodePredicate = mitk::TNodePredicateDataType::New().GetPointer(); + mitk::TNodePredicateDataType::ConstPointer InternalPointSetNodePredicate = mitk::TNodePredicateDataType::New().GetPointer(); MITKRegistrationHelper::Affine3DTransformType::Pointer MITKRegistrationHelper:: getAffineMatrix(const mitk::MAPRegistrationWrapper* wrapper, bool inverseKernel) { Affine3DTransformType::Pointer result = nullptr; if (wrapper) { result = getAffineMatrix(wrapper->GetRegistration(), inverseKernel); } return result; } MITKRegistrationHelper::Affine3DTransformType::Pointer MITKRegistrationHelper:: getAffineMatrix(const RegistrationBaseType* registration, bool inverseKernel) { Affine3DTransformType::Pointer result = nullptr; if (registration && is3D(registration)) { const Registration3DType* pReg = dynamic_cast(registration); if (pReg) { if (inverseKernel) { result = getAffineMatrix(pReg->getInverseMapping()); } else { result = getAffineMatrix(pReg->getDirectMapping()); } } } return result; } MITKRegistrationHelper::Affine3DTransformType::Pointer MITKRegistrationHelper::getAffineMatrix(const RegistrationKernel3DBase& kernel) { Affine3DTransformType::Pointer result = nullptr; typedef ::map::core::RegistrationKernel<3,3> KernelType; const KernelType* pModelKernel = dynamic_cast(&kernel); if (pModelKernel) { KernelType::TransformType::MatrixType matrix; KernelType::TransformType::OutputVectorType offset; if(pModelKernel->getAffineMatrixDecomposition(matrix,offset)) { result = Affine3DTransformType::New(); Affine3DTransformType::MatrixType resultMatrix; Affine3DTransformType::OffsetType resultOffset; /**@TODO If MatchPoint and MITK get same scalar values the casting of matrix and offset values is obsolete and should be removed.*/ //The conversion of matrix and offset is needed //because mitk uses float and MatchPoint currently //double as scalar value. for (unsigned int i=0; i(matrix.GetVnlMatrix().begin()[i]); } resultOffset.CastFrom(offset); //needed because mitk uses float and MatchPoint currently double as scalar value result->SetMatrix(resultMatrix); result->SetOffset(resultOffset); } } return result; } bool MITKRegistrationHelper::is3D(const mitk::MAPRegistrationWrapper* wrapper) { bool result = false; if (wrapper) { result = wrapper->GetMovingDimensions()==3 && wrapper->GetTargetDimensions()==3; } return result; } bool MITKRegistrationHelper::is3D(const RegistrationBaseType* reBase) { bool result = false; if (reBase) { result = reBase->getMovingDimensions()==3 && reBase->getTargetDimensions()==3; } return result; } bool MITKRegistrationHelper::IsRegNode(const mitk::DataNode* node) { if (!node) return false; return InternalRegNodePredicate->CheckNode(node); } NodePredicateBase::ConstPointer MITKRegistrationHelper::RegNodePredicate() { return InternalRegNodePredicate.GetPointer(); } NodePredicateBase::ConstPointer MITKRegistrationHelper::ImageNodePredicate() { return InternalImageNodePredicate.GetPointer(); } NodePredicateBase::ConstPointer MITKRegistrationHelper::PointSetNodePredicate() { return InternalPointSetNodePredicate.GetPointer(); } + NodePredicateBase::ConstPointer MITKRegistrationHelper::MaskNodePredicate() + { + auto isLabelSetImage = mitk::NodePredicateDataType::New("LabelSetImage"); + auto hasBinaryProperty = mitk::NodePredicateProperty::New("binary", mitk::BoolProperty::New(true)); + auto isLegacyMask = mitk::NodePredicateAnd::New(ImageNodePredicate(), hasBinaryProperty); + + return isLegacyMask.GetPointer(); + //Deactivated due to T27435. Should be reactivated as soon T27435 is fixed + //auto isLabelSetOrLegacyMask = mitk::NodePredicateOr::New(isLabelSetImage, isLegacyMask); + // + //return isLabelSetOrLegacyMask.GetPointer(); + } + } diff --git a/Modules/MatchPointRegistration/Helper/mitkRegistrationHelper.h b/Modules/MatchPointRegistration/Helper/mitkRegistrationHelper.h index 03932c533f..ff3f1c4c5d 100644 --- a/Modules/MatchPointRegistration/Helper/mitkRegistrationHelper.h +++ b/Modules/MatchPointRegistration/Helper/mitkRegistrationHelper.h @@ -1,88 +1,91 @@ /*============================================================================ 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 _mitkRegistrationHelper_h #define _mitkRegistrationHelper_h //ITK #include "itkScalableAffineTransform.h" //MatchPoint #include "mapRegistrationAlgorithmBase.h" #include "mapRegistration.h" //MITK #include #include #include //MITK #include "MitkMatchPointRegistrationExports.h" #include "mitkMAPRegistrationWrapper.h" namespace mitk { /*! \brief MITKRegistrationHelper \warning This class is not yet documented. Use "git blame" and ask the author to provide basic documentation. */ class MITKMATCHPOINTREGISTRATION_EXPORT MITKRegistrationHelper { public: typedef ::itk::ScalableAffineTransform< ::mitk::ScalarType,3 > Affine3DTransformType; typedef ::map::core::Registration<3,3> Registration3DType; typedef ::map::core::RegistrationBase RegistrationBaseType; /** Extracts the affine transformation, if possible, of the selected kernel. @param wrapper Pointer to the registration that is target of the extraction @param inverseKernel Indicates from which kernel the matrix should be extract. True: inverse kernel, False: direct kernel. @return Pointer to the extracted transform. If it is not possible to convert the kernel into an affine transform a null pointer is returned. @pre wrapper must point to a valid instance. @pre wrapper must be a 3D-3D registration.*/ static Affine3DTransformType::Pointer getAffineMatrix(const mitk::MAPRegistrationWrapper* wrapper, bool inverseKernel); static Affine3DTransformType::Pointer getAffineMatrix(const RegistrationBaseType* registration, bool inverseKernel); static bool is3D(const mitk::MAPRegistrationWrapper* wrapper); static bool is3D(const RegistrationBaseType* regBase); /** Checks if the passed Node contains a MatchPoint registration @param Pointer to the node to be checked.* @return true: node contains a MAPRegistrationWrapper. false: "node" does not point to a valid instance or does not contain a registration wrapper.*/; static bool IsRegNode(const mitk::DataNode* node); /** Returns a node predicate that identifies registration nodes.*/ static NodePredicateBase::ConstPointer RegNodePredicate(); /** Returns a node predicate that identifies image nodes.*/ static NodePredicateBase::ConstPointer ImageNodePredicate(); + /** Returns a node predicate that identifies segmentation/mask nodes.*/ + static NodePredicateBase::ConstPointer MaskNodePredicate(); + /** Returns a node predicate that identifies point set nodes.*/ static NodePredicateBase::ConstPointer PointSetNodePredicate(); private: typedef ::map::core::Registration<3,3>::DirectMappingType RegistrationKernel3DBase; static Affine3DTransformType::Pointer getAffineMatrix(const RegistrationKernel3DBase& kernel); MITKRegistrationHelper(); ~MITKRegistrationHelper(); MITKRegistrationHelper& operator = (const MITKRegistrationHelper&); MITKRegistrationHelper(const MITKRegistrationHelper&); }; } #endif diff --git a/Modules/MatchPointRegistration/Helper/mitkTimeFramesRegistrationHelper.cpp b/Modules/MatchPointRegistration/Helper/mitkTimeFramesRegistrationHelper.cpp index 6c18fa1dc2..5950e7ba52 100644 --- a/Modules/MatchPointRegistration/Helper/mitkTimeFramesRegistrationHelper.cpp +++ b/Modules/MatchPointRegistration/Helper/mitkTimeFramesRegistrationHelper.cpp @@ -1,234 +1,234 @@ /*============================================================================ 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 "itkCommand.h" #include "mitkTimeFramesRegistrationHelper.h" #include #include #include -#include +#include mitk::Image::Pointer mitk::TimeFramesRegistrationHelper::GetFrameImage(const mitk::Image* image, mitk::TimePointType timePoint) const { mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(timePoint); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::Image::Pointer frameImage = imageTimeSelector->GetOutput(); return frameImage; }; void mitk::TimeFramesRegistrationHelper::Generate() { CheckValidInputs(); //prepare processing mitk::Image::Pointer targetFrame = GetFrameImage(this->m_4DImage, 0); this->m_Registered4DImage = this->m_4DImage->Clone(); Image::ConstPointer mask; if (m_TargetMask.IsNotNull()) { if (m_TargetMask->GetTimeSteps() > 1) { mask = GetFrameImage(m_TargetMask, 0); } else { mask = m_TargetMask; } } double progressDelta = 1.0 / ((this->m_4DImage->GetTimeSteps() - 1) * 3.0); m_Progress = 0.0; //process the frames for (unsigned int i = 1; i < this->m_4DImage->GetTimeSteps(); ++i) { Image::Pointer movingFrame = GetFrameImage(this->m_4DImage, i); Image::Pointer mappedFrame; IgnoreListType::iterator finding = std::find(m_IgnoreList.begin(), m_IgnoreList.end(), i); if (finding == m_IgnoreList.end()) { //frame should be processed RegistrationPointer reg = DoFrameRegistration(movingFrame, targetFrame, mask); m_Progress += progressDelta; this->InvokeEvent(::mitk::FrameRegistrationEvent(nullptr, "Registred frame #" +::map::core::convert::toStr(i))); mappedFrame = DoFrameMapping(movingFrame, reg, targetFrame); m_Progress += progressDelta; this->InvokeEvent(::mitk::FrameMappingEvent(nullptr, "Mapped frame #" + ::map::core::convert::toStr(i))); mitk::ImageReadAccessor accessor(mappedFrame, mappedFrame->GetVolumeData(0, 0, nullptr, mitk::Image::ReferenceMemory)); this->m_Registered4DImage->SetVolume(accessor.GetData(), i); this->m_Registered4DImage->GetTimeGeometry()->SetTimeStepGeometry(mappedFrame->GetGeometry(), i); m_Progress += progressDelta; } else { m_Progress += 3 * progressDelta; } this->InvokeEvent(::itk::ProgressEvent()); } }; mitk::Image::Pointer mitk::TimeFramesRegistrationHelper::GetRegisteredImage() { if (this->HasOutdatedResult()) { Generate(); } return m_Registered4DImage; }; void mitk::TimeFramesRegistrationHelper:: SetIgnoreList(const IgnoreListType& il) { m_IgnoreList = il; this->Modified(); } void mitk::TimeFramesRegistrationHelper::ClearIgnoreList() { m_IgnoreList.clear(); this->Modified(); }; mitk::TimeFramesRegistrationHelper::RegistrationPointer mitk::TimeFramesRegistrationHelper::DoFrameRegistration(const mitk::Image* movingFrame, const mitk::Image* targetFrame, const mitk::Image* targetMask) const { - mitk::MITKAlgorithmHelper algHelper(m_Algorithm); + mitk::MAPAlgorithmHelper algHelper(m_Algorithm); algHelper.SetAllowImageCasting(true); algHelper.SetData(movingFrame, targetFrame); if (targetMask) { mitk::MaskedAlgorithmHelper maskHelper(m_Algorithm); maskHelper.SetMasks(nullptr, targetMask); } return algHelper.GetRegistration(); }; mitk::Image::Pointer mitk::TimeFramesRegistrationHelper::DoFrameMapping( const mitk::Image* movingFrame, const RegistrationType* reg, const mitk::Image* targetFrame) const { return mitk::ImageMappingHelper::map(movingFrame, reg, !m_AllowUndefPixels, m_PaddingValue, targetFrame->GetGeometry(), !m_AllowUnregPixels, m_ErrorValue, m_InterpolatorType); }; bool mitk::TimeFramesRegistrationHelper::HasOutdatedResult() const { if (m_Registered4DImage.IsNull()) { return true; } bool result = false; if (m_Registered4DImage->GetMTime() > this->GetMTime()) { result = true; } if (m_Algorithm.IsNotNull()) { if (m_Algorithm->GetMTime() > this->GetMTime()) { result = true; } } if (m_4DImage.IsNotNull()) { if (m_4DImage->GetMTime() > this->GetMTime()) { result = true; } } if (m_TargetMask.IsNotNull()) { if (m_TargetMask->GetMTime() > this->GetMTime()) { result = true; } } return result; }; void mitk::TimeFramesRegistrationHelper::CheckValidInputs() const { if (m_4DImage.IsNull()) { mitkThrow() << "Cannot register image. Input 4D image is not set."; } if (m_Algorithm.IsNull()) { mitkThrow() << "Cannot register image. Algorithm is not set."; } if (m_4DImage->GetTimeSteps() <= 1) { mitkThrow() << "Cannot register image. Input 4D image must have 2 or more time steps."; } for (IgnoreListType::const_iterator pos = this->m_IgnoreList.begin(); pos != this->m_IgnoreList.end(); ++pos) { if (*pos >= m_4DImage->GetTimeSteps()) { mitkThrow() << "Cannot register image. Ignore list containes at least one inexistant frame. Invalid frame index: " << *pos; } } }; double mitk::TimeFramesRegistrationHelper::GetProgress() const { return m_Progress; }; diff --git a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp index 8e809e044b..c5cf757609 100644 --- a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp +++ b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp @@ -1,280 +1,279 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include "mapRegistration.h" #include "mapRegistrationFileWriter.h" #include "mapRegistrationFileReader.h" #include "mapLazyFileFieldKernelLoader.h" #include #include #include #include "mitkMAPRegistrationWrapperIO.h" #include "mitkMAPRegistrationWrapper.h" namespace mitk { /** Helper class that allows to use an functor in multiple combinations of * moving and target dimensions on a passed MAPRegistrationWrapper instance.\n * DimHelperSub is used DimHelper to iterate in a row of the dimension * combination matrix. */ template< unsigned int i, unsigned int j, template < unsigned int, unsigned int> class TFunctor> class DimHelperSub { public: static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data) { if (TFunctor::Execute(obj, data)) { return true; } return DimHelperSub::Execute(obj, data); } }; /** Specialized template version of DimSubHelper that indicates the end * of the row in the dimension combination matrix, thus does nothing. */ template< unsigned int i, template < unsigned int, unsigned int> class TFunctor> class DimHelperSub { public: static bool Execute(const mitk::MAPRegistrationWrapper*, const map::core::String&) { //just unwind. Go to the next "row" with DimHelper return false; } }; /** Helper class that allows to use an functor in multiple combinations of * moving and target dimensions on a passed MAPRegistrationWrapper instance.\n * It is helpful if you want to ensure that all combinations are checked/touched * (e.g. 3D 3D, 3D 2D, 2D 3D, 2D 2D) without generating a large switch yard. * Think of n*m matrix (indicating the posible combinations). DimHelper walks from * one row to the next and uses DimHelperSub to iterate in a row.\n * For every element of the matrix the functor is executed on the passed object. */ template< unsigned int i, unsigned int j, template < unsigned int, unsigned int> class TFunctor> class DimHelper{ public: static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data = "") { if (DimHelperSub::Execute(obj, data)) { return true; } return DimHelper::Execute(obj, data); } }; /** Specialized template version of DimHelper that indicates the end * of the dimension combination matrix, thus does nothing. */ template< unsigned int j, template < unsigned int, unsigned int> class TFunctor> class DimHelper<1,j, TFunctor > { public: static bool Execute(const mitk::MAPRegistrationWrapper*, const map::core::String&) { //just unwind. We are done. return false; } }; /** Functor that checks of the dimension of the registration is supported and can * be written. */ template class CanWrite { public: static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& = "") { bool result = false; result = dynamic_cast *>(obj->GetRegistration()) != nullptr; return result; } }; /** Functor that writes the registration to a file if it has the right dimensionality. */ template class WriteReg { public: static bool Execute(const mitk::MAPRegistrationWrapper* obj, const map::core::String& data) { const map::core::Registration* pReg = dynamic_cast*>(obj->GetRegistration()); if (pReg == nullptr) { return false; } typedef map::io::RegistrationFileWriter WriterType; typename WriterType::Pointer writer = WriterType::New(); writer->setExpandLazyKernels(false); try { writer->write(pReg,data); } catch (const itk::ExceptionObject& e) { std::cout << e.what() << std::endl; throw; } return true; } }; MAPRegistrationWrapperIO::MAPRegistrationWrapperIO(const MAPRegistrationWrapperIO& other) : AbstractFileIO(other) { } MAPRegistrationWrapperIO::MAPRegistrationWrapperIO() : AbstractFileIO(mitk::MAPRegistrationWrapper::GetStaticNameOfClass()) { std::string category = "MatchPoint Registration File"; CustomMimeType customMimeType; customMimeType.SetCategory(category); customMimeType.AddExtension("mapr"); this->AbstractFileIOWriter::SetMimeType(customMimeType); this->AbstractFileIOWriter::SetDescription(category); customMimeType.AddExtension("mapr.xml"); customMimeType.AddExtension("MAPR"); customMimeType.AddExtension("MAPR.XML"); this->AbstractFileIOReader::SetMimeType(customMimeType); this->AbstractFileIOReader::SetDescription(category); this->RegisterService(); } void MAPRegistrationWrapperIO::Write() { bool success = false; const BaseData* input = this->GetInput(); if (input == nullptr) { mitkThrow() << "Cannot write data. Data pointer is nullptr."; } const mitk::MAPRegistrationWrapper* wrapper = dynamic_cast(input); if (wrapper == nullptr) { mitkThrow() << "Cannot write data. Data pointer is not a Registration wrapper."; } std::ostream* writeStream = this->GetOutputStream(); std::string fileName = this->GetOutputLocation(); if (writeStream) { fileName = this->GetLocalFileName(); } // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); try { success = DimHelper<3,3,WriteReg>::Execute(wrapper, fileName); } catch (const std::exception& e) { mitkThrow() << e.what(); } if (!success) { mitkThrow() << "Cannot write registration. Currently only registrations up to 4D are supported."; } } AbstractFileIO::ConfidenceLevel MAPRegistrationWrapperIO::GetWriterConfidenceLevel() const { const mitk::MAPRegistrationWrapper* regWrapper = dynamic_cast(this->GetInput()); if (regWrapper == nullptr) { return IFileWriter::Unsupported; } // Check if the registration dimension is supported if (! DimHelper<3,3,CanWrite>::Execute(regWrapper)) { return IFileWriter::Unsupported; }; return IFileWriter::Supported; } std::vector MAPRegistrationWrapperIO::DoRead() { std::vector result; LocaleSwitch("C"); std::string fileName = this->GetLocalFileName(); if ( fileName.empty() ) { mitkThrow() << "Cannot read file. Filename has not been set!"; } /* Remove the following kernel loader provider because in MITK no lazy file loading should be used due to conflicts with session loading (end there usage of temporary directories)*/ map::io::RegistrationFileReader::LoaderStackType::unregisterProvider(map::io::LazyFileFieldKernelLoader<2,2>::getStaticProviderName()); map::io::RegistrationFileReader::LoaderStackType::unregisterProvider(map::io::LazyFileFieldKernelLoader<3,3>::getStaticProviderName()); map::io::RegistrationFileReader::Pointer spReader = map::io::RegistrationFileReader::New(); spReader->setPreferLazyLoading(true); map::core::RegistrationBase::Pointer spReg = spReader->read(fileName); - mitk::MAPRegistrationWrapper::Pointer spRegWrapper = mitk::MAPRegistrationWrapper::New(); - spRegWrapper->SetRegistration(spReg); + auto spRegWrapper = mitk::MAPRegistrationWrapper::New(spReg); result.push_back(spRegWrapper.GetPointer()); return result; } AbstractFileIO::ConfidenceLevel MAPRegistrationWrapperIO::GetReaderConfidenceLevel() const { AbstractFileIO::ConfidenceLevel result = IFileReader::Unsupported; std::string fileName = this->GetLocalFileName(); std::ifstream in( fileName.c_str() ); if ( in.good() ) { result = IFileReader::Supported; } in.close(); return result; } MAPRegistrationWrapperIO* MAPRegistrationWrapperIO::IOClone() const { return new MAPRegistrationWrapperIO(*this); } } diff --git a/Modules/MatchPointRegistration/files.cmake b/Modules/MatchPointRegistration/files.cmake index 8546d7de97..1b9c8ff7e1 100644 --- a/Modules/MatchPointRegistration/files.cmake +++ b/Modules/MatchPointRegistration/files.cmake @@ -1,65 +1,65 @@ set(CPP_FILES mitkMAPRegistrationWrapper.cpp mitkMAPRegistrationWrapperObjectFactory.cpp mitkRegEvaluationObjectFactory.cpp mitkRegEvaluationObject.cpp Helper/mitkUIDHelper.cpp - Helper/mitkAlgorithmHelper.cpp + Helper/mitkMAPAlgorithmHelper.cpp Helper/mitkMaskedAlgorithmHelper.cpp Helper/mitkRegistrationHelper.cpp Helper/mitkImageMappingHelper.cpp Helper/mitkPointSetMappingHelper.cpp Helper/mitkResultNodeGenerationHelper.cpp Helper/mitkTimeFramesRegistrationHelper.cpp Rendering/mitkRegistrationWrapperMapper2D.cpp Rendering/mitkRegistrationWrapperMapper3D.cpp Rendering/mitkRegistrationWrapperMapperBase.cpp Rendering/mitkRegEvaluationMapper2D.cpp Rendering/mitkRegVisStyleProperty.cpp Rendering/mitkRegVisDirectionProperty.cpp Rendering/mitkRegVisColorStyleProperty.cpp Rendering/mitkRegVisPropertyTags.cpp Rendering/mitkRegVisHelper.cpp Rendering/mitkRegEvalStyleProperty.cpp Rendering/mitkRegEvalWipeStyleProperty.cpp ) set(H_FILES mitkMatchPointPropertyTags.h mitkMAPRegistrationWrapper.h mitkMAPRegistrationWrapperObjectFactory.h mitkRegEvaluationObjectFactory.h mitkRegEvaluationObject.h algorithms/mitkMultiModalAffineDefaultRegistrationAlgorithm.h algorithms/mitkMultiModalRigidDefaultRegistrationAlgorithm.h algorithms/mitkMultiModalTransDefaultRegistrationAlgorithm.h algorithms/mitkFastSymmetricForcesDemonsMultiResDefaultRegistrationAlgorithm.h algorithms/mitkLevelSetMotionMultiResDefaultRegistrationAlgorithm.h algorithms/mitkRigidClosedFormPointsDefaultRegistrationAlgorithm.h algorithms/mitkRigidICPDefaultRegistrationAlgorithm.h Helper/mitkUIDHelper.h - Helper/mitkAlgorithmHelper.h + Helper/mitkMAPAlgorithmHelper.h Helper/mitkMaskedAlgorithmHelper.h Helper/mitkRegistrationHelper.h Helper/mitkImageMappingHelper.h Helper/mitkPointSetMappingHelper.h Helper/mitkResultNodeGenerationHelper.h Helper/mitkTimeFramesRegistrationHelper.h Rendering/mitkRegistrationWrapperMapper2D.h Rendering/mitkRegistrationWrapperMapper3D.h Rendering/mitkRegistrationWrapperMapperBase.h Rendering/mitkRegVisStyleProperty.h Rendering/mitkRegVisDirectionProperty.h Rendering/mitkRegVisColorStyleProperty.h Rendering/mitkRegVisPropertyTags.h Rendering/mitkRegVisHelper.h Rendering/mitkRegEvaluationMapper2D.h Rendering/mitkRegEvalStyleProperty.h Rendering/mitkRegEvalWipeStyleProperty.h ) set(TPP_FILES ) set(MOC_H_FILES ) diff --git a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.cpp b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.cpp index 616f02f66b..2ad231a33d 100644 --- a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.cpp +++ b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.cpp @@ -1,137 +1,159 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkMAPRegistrationWrapper.h" #include +#include -mitk::MAPRegistrationWrapper::MAPRegistrationWrapper() +mitk::MAPRegistrationWrapper::MAPRegistrationWrapper(map::core::RegistrationBase* registration) : m_spRegistration(registration) { + if (registration == nullptr) + { + mitkThrow() << "Error. Cannot create MAPRegistrationWrapper with invalid registration instance (nullptr)."; + } + + Identifiable::SetUID(registration->getRegistrationUID()); } mitk::MAPRegistrationWrapper::~MAPRegistrationWrapper() { } void mitk::MAPRegistrationWrapper::SetRequestedRegionToLargestPossibleRegion() { //nothing to do } bool mitk::MAPRegistrationWrapper::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::MAPRegistrationWrapper::VerifyRequestedRegion() { return true; } void mitk::MAPRegistrationWrapper::SetRequestedRegion(const itk::DataObject*) { //nothing to do } unsigned int mitk::MAPRegistrationWrapper::GetMovingDimensions() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot return moving dimension. Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->getMovingDimensions(); } unsigned int mitk::MAPRegistrationWrapper::GetTargetDimensions() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot return target dimension. Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->getTargetDimensions(); } const mitk::MAPRegistrationWrapper::TagMapType& mitk::MAPRegistrationWrapper::GetTags() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot return registration tags. Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->getTags(); } bool mitk::MAPRegistrationWrapper::GetTagValue(const TagType & tag, ValueType & value) const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot return registration tag value. Wrapper points to invalid registration (nullptr). Tag: " << tag; } return m_spRegistration->getTagValue(tag,value); } bool mitk::MAPRegistrationWrapper::HasLimitedTargetRepresentation() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot determin HasLimitedTargetRepresentation(). Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->hasLimitedTargetRepresentation(); } bool mitk::MAPRegistrationWrapper::HasLimitedMovingRepresentation() const { if (m_spRegistration.IsNull()) { mitkThrow()<< "Error. Cannot determin HasLimitedMovingRepresentation(). Wrapper points to invalid registration (nullptr)."; } return m_spRegistration->hasLimitedMovingRepresentation(); } map::core::RegistrationBase* mitk::MAPRegistrationWrapper::GetRegistration() { return m_spRegistration; } const map::core::RegistrationBase* mitk::MAPRegistrationWrapper::GetRegistration() const { return m_spRegistration; } -void mitk::MAPRegistrationWrapper::SetRegistration(map::core::RegistrationBase* pReg) -{ - m_spRegistration = pReg; -} - void mitk::MAPRegistrationWrapper::PrintSelf (std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os,indent); if (m_spRegistration.IsNull()) { os<< "Error. Wrapper points to invalid registration (nullptr)."; } else { os<Print(os,indent.GetNextIndent()); typedef map::core::Registration<3,3> CastedRegType; const CastedRegType* pCastedReg = dynamic_cast(m_spRegistration.GetPointer()); os<getDirectMapping().Print(os,indent.GetNextIndent()); os<getInverseMapping().Print(os,indent.GetNextIndent()); } } + +void mitk::MAPRegistrationWrapper::SetUID(const UIDType& uid) +{ + if (m_spRegistration.IsNull()) + { + mitkThrow() << "Error. Cannot set UID. Wrapper points to invalid registration (nullptr)."; + } + Identifiable::SetUID(uid); + ::map::core::RegistrationBaseManipulator manip(m_spRegistration); + manip.getTagValues()[::map::tags::RegistrationUID] = uid; +}; + +mitk::Identifiable::UIDType mitk::MAPRegistrationWrapper::GetUID() const +{ + if (m_spRegistration.IsNull()) + { + mitkThrow() << "Error. Cannot return UID. Wrapper points to invalid registration (nullptr)."; + } + return m_spRegistration->getRegistrationUID(); +}; diff --git a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.h b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.h index ece571bede..734392a845 100644 --- a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.h +++ b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapper.h @@ -1,268 +1,270 @@ /*============================================================================ 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 mitkMAPRegistrationWrapper_h #define mitkMAPRegistrationWrapper_h //MITK #include #include //MatchPoint #include #include #include #include //MITK #include "MitkMatchPointRegistrationExports.h" namespace mitk { /*! \brief MAPRegistrationWrapper Wrapper class to allow the handling of MatchPoint registration objects as mitk data (e.g. in the data explorer). */ class MITKMATCHPOINTREGISTRATION_EXPORT MAPRegistrationWrapper: public mitk::BaseData { public: mitkClassMacro( MAPRegistrationWrapper, BaseData ); - itkNewMacro( Self ); + mitkNewMacro1Param( Self, ::map::core::RegistrationBase*); + + Identifiable::UIDType GetUID() const override; /** * Empty implementation, since the MAPRegistrationWrapper doesn't * support the requested region concept */ void SetRequestedRegionToLargestPossibleRegion() override; /** * Empty implementation, since the MAPRegistrationWrapper doesn't * support the requested region concept */ bool RequestedRegionIsOutsideOfTheBufferedRegion() override; /** * Empty implementation, since the MAPRegistrationWrapper doesn't * support the requested region concept */ bool VerifyRequestedRegion() override; /** * Empty implementation, since the MAPRegistrationWrapper doesn't * support the requested region concept */ void SetRequestedRegion(const itk::DataObject*) override; /*! @brief Gets the number of moving dimensions @pre valid registration instance must be set. */ virtual unsigned int GetMovingDimensions() const; /*! @brief Gets the number of target dimensions @pre valid registration instance must be set. */ virtual unsigned int GetTargetDimensions() const; /*! typedefs used for the TagMap */ typedef ::map::core::RegistrationBase::TagType TagType; typedef ::map::core::RegistrationBase::ValueType ValueType; typedef ::map::core::RegistrationBase::TagMapType TagMapType; /*! @brief returns the tags associated with this registration @pre valid registration instance must be set. @return a TagMapType containing tags */ const TagMapType& GetTags() const; /*! @brief returns the tag value for a specific tag @pre valid registration instance must be set. @return the success of the operation */ bool GetTagValue(const TagType & tag, ValueType & value) const; /*! Indicates @pre valid registration instance must be set. @return is the target representation limited @retval true if target representation is limited. Thus it is not guaranteed that all inverse mapping operations will succeed. Transformation(inverse kernel) covers only a part of the target space). @retval false if target representation is not limited. Thus it is guaranteed that all inverse mapping operations will succeed. */ bool HasLimitedTargetRepresentation() const; /*! @pre valid registration instance must be set. @return is the moving representation limited @retval true if moving representation is limited. Thus it is not guaranteed that all direct mapping operations will succeed. Transformation(direct kernel) covers only a part of the moving space). @retval false if moving representation is not limited. Thus it is guaranteed that all direct mapping operations will succeed. */ bool HasLimitedMovingRepresentation() const; /*! Helper function that maps a mitk point (of arbitrary dimension) from moving space to target space. @remarks The operation might fail, if the moving and target dimension of the registration is not equal to the dimensionality of the passed points. @pre valid registration instance must be set. @param inPoint Reference pointer to a MovingPointType @param outPoint pointer to a TargetPointType @return success of operation. @pre direct mapping kernel must be defined */ template bool MapPoint(const ::itk::Point& inPoint, ::itk::Point& outPoint) const { typedef typename ::map::core::continuous::Elements::PointType MAPMovingPointType; typedef typename ::map::core::continuous::Elements::PointType MAPTargetPointType; if (m_spRegistration.IsNull()) { mapDefaultExceptionMacro(<< "Error. Cannot map point. Wrapper points to invalid registration (nullptr). Point: " << inPoint); } bool result = false; if ((this->GetMovingDimensions() == VMovingDim)&&(this->GetTargetDimensions() == VTargetDim)) { MAPMovingPointType tempInP; MAPTargetPointType tempOutP; tempInP.CastFrom(inPoint); typedef ::map::core::Registration CastedRegType; const CastedRegType* pCastedReg = dynamic_cast(m_spRegistration.GetPointer()); if (!pCastedReg) { mapDefaultExceptionMacro(<< "Error. Cannot map point. Registration has invalid dimension. Point: " << inPoint); } result = pCastedReg->mapPoint(tempInP,tempOutP); if (result) { outPoint.CastFrom(tempOutP); } } return result; }; /*! Helper function that maps a mitk point (of arbitrary dimension) from target space to moving space @remarks The operation might faile, if the moving and target dimension of the registration is not equal to the dimensionalities of the passed points. @pre valid registration instance must be set. @param inPoint pointer to a TargetPointType @param outPoint pointer to a MovingPointType @return success of operation */ template bool MapPointInverse(const ::itk::Point & inPoint, ::itk::Point & outPoint) const { typedef typename ::map::core::continuous::Elements::PointType MAPMovingPointType; typedef typename ::map::core::continuous::Elements::PointType MAPTargetPointType; if (m_spRegistration.IsNull()) { mapDefaultExceptionMacro(<< "Error. Cannot map point. Wrapper points to invalid registration (nullptr). Point: " << inPoint); } bool result = false; if ((this->GetMovingDimensions() == VMovingDim)&&(this->GetTargetDimensions() == VTargetDim)) { MAPTargetPointType tempInP; MAPMovingPointType tempOutP; tempInP.CastFrom(inPoint); typedef ::map::core::Registration CastedRegType; const CastedRegType* pCastedReg = dynamic_cast(m_spRegistration.GetPointer()); if (!pCastedReg) { mapDefaultExceptionMacro(<< "Error. Cannot map point. Registration has invalid dimension. Point: " << inPoint); } result = pCastedReg->mapPointInverse(tempInP,tempOutP); if (result) { outPoint.CastFrom(tempOutP); } } return result; }; /*! returns the direct FieldRepresentationDescriptor which defines the part of the moving space that is guaranteed to be mapped by the direct mapping kernel. This member converts the internal MatchPoint type into a mitk::Geometry3D. @pre valid registration instance must be set. @return smart pointer to a FieldRepresentationDescriptor for the supported registration space in the moving domain. May be null if the direct registration kernel is global and thus not limited. If there is a limitation, the retun value is not nullptr. @retval nullptr no field representation set/requested by the creating registration algorithm. */ mitk::Geometry3D GetDirectFieldRepresentation() const; /*! returns the inverse FieldRepresentationDescriptor which defines the part of the target space that is guaranteed to be mapped by the inverse mapping kernel. This member converts the internal MatchPoint type into a mitk::Geometry3D. @pre valid registration instance must be set. @return a const FieldRepresentationDescriptor for the supported registration space in the target domain. May be null if the inverse registration kernel is global and thus not limited. If there is a limitation, the retun value is not nullptr. @retval nullptr no field representation set/requested by the creating registration algorithm. */ mitk::Geometry3D GetInverseFieldRepresentation() const; /*! forces kernel to precompute, even if it is a LazyFieldKernel @pre valid registration instance must be set. @todo der LazyFieldBasedRegistrationKernel muss dann die stong guarantee erfllen beim erzeugen des feldes ansonsten ist die garantie dieser methode nicht erfllbar. noch berprfen */ void PrecomputeDirectMapping(); /*! forces kernel to precompute, even if it is a LazyFieldKernel @pre valid registration instance must be set. @todo der LazyFieldBasedRegistrationKernel muss dann die stong guarantee erfllen beim erzeugen des feldes ansonsten ist die garantie dieser methode nicht erfllbar. noch berprfen */ void PrecomputeInverseMapping(); ::map::core::RegistrationBase* GetRegistration(); const ::map::core::RegistrationBase* GetRegistration() const; - void SetRegistration(::map::core::RegistrationBase* pReg); - protected: void PrintSelf (std::ostream &os, itk::Indent indent) const override; - MAPRegistrationWrapper(); + MAPRegistrationWrapper(::map::core::RegistrationBase* registration); ~MAPRegistrationWrapper() override; + void SetUID(const UIDType& uid) override; + ::map::core::RegistrationBase::Pointer m_spRegistration; private: MAPRegistrationWrapper& operator = (const MAPRegistrationWrapper&); MAPRegistrationWrapper(const MAPRegistrationWrapper&); }; } #endif diff --git a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapperObjectFactory.cpp b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapperObjectFactory.cpp index ef826f915d..a76440f531 100644 --- a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapperObjectFactory.cpp +++ b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapperObjectFactory.cpp @@ -1,123 +1,123 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkMAPRegistrationWrapperObjectFactory.h" #include #include #include #include "mitkRegistrationWrapperMapper2D.h" #include "mitkRegistrationWrapperMapper3D.h" #include "mitkMAPRegistrationWrapper.h" typedef std::multimap MultimapType; mitk::MAPRegistrationWrapperObjectFactory::MAPRegistrationWrapperObjectFactory() : CoreObjectFactoryBase() { static bool alreadyDone = false; if (!alreadyDone) { alreadyDone = true; } } mitk::MAPRegistrationWrapperObjectFactory::~MAPRegistrationWrapperObjectFactory() { } mitk::Mapper::Pointer mitk::MAPRegistrationWrapperObjectFactory:: CreateMapper(mitk::DataNode* node, MapperSlotId slotId) { mitk::Mapper::Pointer newMapper=nullptr; if ( slotId == mitk::BaseRenderer::Standard2D ) { std::string classname("MAPRegistrationWrapper"); if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) { newMapper = mitk::MITKRegistrationWrapperMapper2D::New(); newMapper->SetDataNode(node); } } else if ( slotId == mitk::BaseRenderer::Standard3D ) { std::string classname("MAPRegistrationWrapper"); if(node->GetData() && classname.compare(node->GetData()->GetNameOfClass())==0) { newMapper = mitk::MITKRegistrationWrapperMapper3D::New(); newMapper->SetDataNode(node); } } return newMapper; }; void mitk::MAPRegistrationWrapperObjectFactory::SetDefaultProperties(mitk::DataNode* node) { if(node==nullptr) return; mitk::DataNode::Pointer nodePointer = node; if(node->GetData() ==nullptr) return; if( dynamic_cast(node->GetData())!=nullptr ) { mitk::MITKRegistrationWrapperMapperBase::SetDefaultProperties(node); } } const char* mitk::MAPRegistrationWrapperObjectFactory::GetFileExtensions() { std::string fileExtension; - this->CreateFileExtensions(m_FileExtensionsMap, fileExtension); + this->CreateFileExtensions({}, fileExtension); return fileExtension.c_str(); }; mitk::CoreObjectFactoryBase::MultimapType mitk::MAPRegistrationWrapperObjectFactory::GetFileExtensionsMap() { - return m_FileExtensionsMap; + return {}; } const char* mitk::MAPRegistrationWrapperObjectFactory::GetSaveFileExtensions() { std::string fileExtension; - this->CreateFileExtensions(m_SaveFileExtensionsMap, fileExtension); + this->CreateFileExtensions({}, fileExtension); return fileExtension.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::MAPRegistrationWrapperObjectFactory::GetSaveFileExtensionsMap() { - return m_SaveFileExtensionsMap; + return {}; } struct RegisterMAPRegistrationWrapperObjectFactoryHelper{ RegisterMAPRegistrationWrapperObjectFactoryHelper() : m_Factory( mitk::MAPRegistrationWrapperObjectFactory::New() ) { mitk::CoreObjectFactory::GetInstance()->RegisterExtraFactory( m_Factory ); } ~RegisterMAPRegistrationWrapperObjectFactoryHelper() { mitk::CoreObjectFactory::GetInstance()->UnRegisterExtraFactory( m_Factory ); } mitk::MAPRegistrationWrapperObjectFactory::Pointer m_Factory; }; static RegisterMAPRegistrationWrapperObjectFactoryHelper registerMITKRegistrationWrapperIOFactoryHelper; diff --git a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapperObjectFactory.h b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapperObjectFactory.h index e7e2b93140..79f9a88e16 100644 --- a/Modules/MatchPointRegistration/mitkMAPRegistrationWrapperObjectFactory.h +++ b/Modules/MatchPointRegistration/mitkMAPRegistrationWrapperObjectFactory.h @@ -1,67 +1,58 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkMAPRegistrationWrapperObjectFactory_h #define __mitkMAPRegistrationWrapperObjectFactory_h #include #include "MitkMatchPointRegistrationExports.h" namespace mitk { class MAPRegistrationWrapperObjectFactory : public mitk::CoreObjectFactoryBase { public: mitkClassMacro(MAPRegistrationWrapperObjectFactory,CoreObjectFactoryBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); ~MAPRegistrationWrapperObjectFactory() override; void SetDefaultProperties(mitk::DataNode* node) override; /** * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ DEPRECATED(virtual const char* GetFileExtensions()); /** * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ DEPRECATED(virtual mitk::CoreObjectFactoryBase::MultimapType GetFileExtensionsMap()); /** * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ DEPRECATED(virtual const char* GetSaveFileExtensions()); /** * @deprecatedSince{2014_10} See mitk::FileWriterRegistry and QmitkIOUtil */ DEPRECATED(virtual mitk::CoreObjectFactoryBase::MultimapType GetSaveFileExtensionsMap()); mitk::Mapper::Pointer CreateMapper(mitk::DataNode* node, MapperSlotId slotId) override; protected: MAPRegistrationWrapperObjectFactory(); - private: - void CreateFileExtensionsMap(); - std::string m_ExternalFileExtensions; - std::string m_InternalFileExtensions; - std::string m_SaveFileExtensions; - MultimapType m_FileExtensionsMap; - MultimapType m_SaveFileExtensionsMap; - - itk::ObjectFactoryBase::Pointer m_RegistrationWrapperIOFactory; }; } #endif diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp index 885e7acfdb..76197de9b9 100644 --- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp +++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationJob.cpp @@ -1,201 +1,200 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkRegistrationJob.h" // Mitk -#include +#include #include #include #include #include #include #include // Qt #include // MatchPoint #include #include #include #include #include const mitk::Image *QmitkRegistrationJob::GetTargetDataAsImage() const { return dynamic_cast(m_spTargetData.GetPointer()); -}; +} const mitk::Image *QmitkRegistrationJob::GetMovingDataAsImage() const { return dynamic_cast(m_spMovingData.GetPointer()); -}; +} const map::algorithm::RegistrationAlgorithmBase *QmitkRegistrationJob::GetLoadedAlgorithm() const { return m_spLoadedAlgorithm; -}; +} void QmitkRegistrationJob::OnMapAlgorithmEvent(::itk::Object *, const itk::EventObject &event) { const map::events::AlgorithmEvent *pAlgEvent = dynamic_cast(&event); const map::events::AlgorithmIterationEvent *pIterationEvent = dynamic_cast(&event); const map::events::AlgorithmWrapperEvent *pWrapEvent = dynamic_cast(&event); const map::events::AlgorithmResolutionLevelEvent *pLevelEvent = dynamic_cast(&event); const map::events::InitializingAlgorithmEvent *pInitEvent = dynamic_cast(&event); const map::events::StartingAlgorithmEvent *pStartEvent = dynamic_cast(&event); const map::events::StoppingAlgorithmEvent *pStoppingEvent = dynamic_cast(&event); const map::events::StoppedAlgorithmEvent *pStoppedEvent = dynamic_cast(&event); const map::events::FinalizingAlgorithmEvent *pFinalizingEvent = dynamic_cast(&event); const map::events::FinalizedAlgorithmEvent *pFinalizedEvent = dynamic_cast(&event); if (pInitEvent) { emit AlgorithmStatusChanged(QString("Initializing algorithm ...")); } else if (pStartEvent) { emit AlgorithmStatusChanged(QString("Starting algorithm ...")); } else if (pStoppingEvent) { emit AlgorithmStatusChanged(QString("Stopping algorithm ...")); } else if (pStoppedEvent) { emit AlgorithmStatusChanged(QString("Stopped algorithm ...")); if (!pStoppedEvent->getComment().empty()) { emit AlgorithmInfo(QString("Stopping condition: ") + QString::fromStdString(pStoppedEvent->getComment())); } } else if (pFinalizingEvent) { emit AlgorithmStatusChanged(QString("Finalizing algorithm and results ...")); } else if (pFinalizedEvent) { emit AlgorithmStatusChanged(QString("Finalized algorithm ...")); } else if (pIterationEvent) { const IIterativeAlgorithm *pIterative = dynamic_cast(this->m_spLoadedAlgorithm.GetPointer()); map::algorithm::facet::IterativeAlgorithmInterface::IterationCountType count = 0; bool hasCount = false; if (pIterative && pIterative->hasIterationCount()) { hasCount = true; count = pIterative->getCurrentIteration(); } emit AlgorithmIterated(QString::fromStdString(pIterationEvent->getComment()), hasCount, count); } else if (pLevelEvent) { const IMultiResAlgorithm *pResAlg = dynamic_cast(this->m_spLoadedAlgorithm.GetPointer()); map::algorithm::facet::MultiResRegistrationAlgorithmInterface::ResolutionLevelCountType count = 0; bool hasCount = false; QString info = QString::fromStdString(pLevelEvent->getComment()); if (pResAlg && pResAlg->hasLevelCount()) { count = pResAlg->getCurrentLevel() + 1; hasCount = true; info = QString("Level #") + QString::number(pResAlg->getCurrentLevel() + 1) + QString(" ") + info; } emit LevelChanged(info, hasCount, count); } else if (pAlgEvent && !pWrapEvent) { emit AlgorithmInfo(QString::fromStdString(pAlgEvent->getComment())); } } QmitkRegistrationJob::QmitkRegistrationJob(map::algorithm::RegistrationAlgorithmBase *pAlgorithm) { m_MapEntity = false; m_StoreReg = false; m_ErrorOccured = false; m_spLoadedAlgorithm = pAlgorithm; m_JobName = "Unnamed RegJob"; m_MovingDataUID = "Missing moving UID"; m_TargetDataUID = "Missing target UID"; m_spTargetMask = nullptr; m_spMovingMask = nullptr; m_spCommand = ::itk::MemberCommand::New(); m_spCommand->SetCallbackFunction(this, &QmitkRegistrationJob::OnMapAlgorithmEvent); m_ObserverID = m_spLoadedAlgorithm->AddObserver(::map::events::AlgorithmEvent(), m_spCommand); -}; +} QmitkRegistrationJob::~QmitkRegistrationJob() { m_spLoadedAlgorithm->RemoveObserver(m_ObserverID); -}; +} void QmitkRegistrationJob::run() { try { - mitk::MITKAlgorithmHelper helper(m_spLoadedAlgorithm); + mitk::MAPAlgorithmHelper helper(m_spLoadedAlgorithm); mitk::MaskedAlgorithmHelper maskedHelper(m_spLoadedAlgorithm); //*@TODO Data Check and failure handle helper.SetData(this->m_spMovingData, this->m_spTargetData); maskedHelper.SetMasks(this->m_spMovingMask, this->m_spTargetMask); // perform registration m_spResultRegistration = helper.GetRegistration(); // wrap the registration in a data node if (m_spResultRegistration.IsNull()) { emit Error(QString("Error. No registration was determined. No results to store.")); } else { - mitk::MAPRegistrationWrapper::Pointer spRegWrapper = mitk::MAPRegistrationWrapper::New(); - spRegWrapper->SetRegistration(m_spResultRegistration); + auto spRegWrapper = mitk::MAPRegistrationWrapper::New(m_spResultRegistration); emit RegResultIsAvailable(spRegWrapper, this); } } catch (::std::exception &e) { emit Error(QString("Error while registering data. Details: ") + QString::fromLatin1(e.what())); } catch (...) { emit Error(QString("Unkown error when registering data.")); } emit Finished(); -}; +} diff --git a/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationManipulationWidget.cpp b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationManipulationWidget.cpp index c1bcb2bcbb..94280074d4 100644 --- a/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationManipulationWidget.cpp +++ b/Modules/MatchPointRegistrationUI/Qmitk/QmitkRegistrationManipulationWidget.cpp @@ -1,348 +1,352 @@ /*============================================================================ 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. ============================================================================*/ //Qmitk #include "QmitkRegistrationManipulationWidget.h" //MatchPoint #include #include #include #include #include #include #include QmitkRegistrationManipulationWidget::QmitkRegistrationManipulationWidget(QWidget *parent) : QWidget(parent), m_CenterOfRotationIsRelativeToTarget(false), m_internalUpdate(false) { this->setupUi(this); connect(this->slideRotX, SIGNAL(valueChanged(int)), this, SLOT(OnRotXSlideChanged(int))); connect(this->sbRotX, SIGNAL(valueChanged(double)), this, SLOT(OnRotXChanged(double))); connect(this->slideRotY, SIGNAL(valueChanged(int)), this, SLOT(OnRotYSlideChanged(int))); connect(this->sbRotY, SIGNAL(valueChanged(double)), this, SLOT(OnRotYChanged(double))); connect(this->slideRotZ, SIGNAL(valueChanged(int)), this, SLOT(OnRotZSlideChanged(int))); connect(this->sbRotZ, SIGNAL(valueChanged(double)), this, SLOT(OnRotZChanged(double))); connect(this->slideTransX, SIGNAL(valueChanged(int)), this, SLOT(OnTransXSlideChanged(int))); connect(this->sbTransX, SIGNAL(valueChanged(double)), this, SLOT(OnTransXChanged(double))); connect(this->slideTransY, SIGNAL(valueChanged(int)), this, SLOT(OnTransYSlideChanged(int))); connect(this->sbTransY, SIGNAL(valueChanged(double)), this, SLOT(OnTransYChanged(double))); connect(this->slideTransZ, SIGNAL(valueChanged(int)), this, SLOT(OnTransZSlideChanged(int))); connect(this->sbTransZ, SIGNAL(valueChanged(double)), this, SLOT(OnTransZChanged(double))); this->groupScale->setVisible(false); } QmitkRegistrationManipulationWidget::~QmitkRegistrationManipulationWidget() = default; void QmitkRegistrationManipulationWidget::Initialize() { this->ResetTransforms(); this->InitControls(); }; void QmitkRegistrationManipulationWidget::Initialize(MAPRegistrationType* precedingRegistration) { this->ResetTransforms(); this->m_PreRegistration = precedingRegistration; ::map::core::RegistrationManipulator manipulator(m_CurrentRegistration); ::map::core::PreCachedRegistrationKernel<3, 3>::Pointer kernel = ::map::core::PreCachedRegistrationKernel<3, 3>::New(); const map::core::RegistrationKernel<3, 3>* preKernel = dynamic_cast*>(&this->m_PreRegistration->getInverseMapping()); itk::CompositeTransform < ::map::core::continuous::ScalarType, 3>::Pointer compTransform = itk::CompositeTransform < ::map::core::continuous::ScalarType, 3>::New(); compTransform->AddTransform(preKernel->getTransformModel()->Clone()); compTransform->AddTransform(this->m_InverseCurrentTransform); kernel->setTransformModel(compTransform); manipulator.setInverseMapping(kernel); this->InitControls(); }; void QmitkRegistrationManipulationWidget::Initialize(const mitk::Point3D& movingReference, const mitk::Point3D& targetReference) { this->ResetTransforms(); auto offset = targetReference - movingReference; m_DirectCurrentTransform->SetOffset(offset); m_DirectCurrentTransform->GetInverse(m_InverseCurrentTransform); this->InitControls(); }; void QmitkRegistrationManipulationWidget::ResetTransforms() { this->m_CenterOfRotation.Fill(0.0); this->m_PreRegistration = nullptr; this->m_InverseCurrentTransform = TransformType::New(); this->m_InverseCurrentTransform->SetIdentity(); this->m_DirectCurrentTransform = TransformType::New(); this->m_DirectCurrentTransform->SetIdentity(); m_CurrentRegistration = MAPRegistrationType::New(); ::map::core::RegistrationManipulator manipulator(m_CurrentRegistration); ::map::core::PreCachedRegistrationKernel<3, 3>::Pointer kernel = ::map::core::PreCachedRegistrationKernel<3, 3>::New(); kernel->setTransformModel(m_InverseCurrentTransform); manipulator.setInverseMapping(kernel); manipulator.setDirectMapping(::map::core::NullRegistrationKernel < 3, 3 >::New()); }; void QmitkRegistrationManipulationWidget::SetCenterOfRotation(const mitk::Point3D& center) { this->m_CenterOfRotation = center; this->ConfigureTransformCenter(); this->UpdateTransformWidgets(); }; /** Sets the internal m_CenterOfRotationIsRelativeToTarget. see below.*/ void QmitkRegistrationManipulationWidget::SetCenterOfRotationIsRelativeToTarget(bool targetRelative) { this->m_CenterOfRotationIsRelativeToTarget = targetRelative; this->ConfigureTransformCenter(); this->UpdateTransformWidgets(); }; void QmitkRegistrationManipulationWidget::InitControls() { this->ConfigureTransformCenter(); //set bounds of the translation slider widget to have sensible ranges + this->m_internalUpdate = true; auto currenttrans = m_DirectCurrentTransform->GetTranslation(); this->slideTransX->setMinimum(currenttrans[0] - 250); this->slideTransY->setMinimum(currenttrans[1] - 250); this->slideTransZ->setMinimum(currenttrans[2] - 250); this->slideTransX->setMaximum(currenttrans[0] + 250); this->slideTransY->setMaximum(currenttrans[1] + 250); this->slideTransZ->setMaximum(currenttrans[2] + 250); + this->m_internalUpdate = false; + + this->UpdateTransformWidgets(); }; void QmitkRegistrationManipulationWidget::UpdateTransformWidgets() { this->m_internalUpdate = true; this->sbTransX->setValue(this->m_DirectCurrentTransform->GetTranslation()[0]); this->sbTransY->setValue(this->m_DirectCurrentTransform->GetTranslation()[1]); this->sbTransZ->setValue(this->m_DirectCurrentTransform->GetTranslation()[2]); this->slideTransX->setValue(this->m_DirectCurrentTransform->GetTranslation()[0]); this->slideTransY->setValue(this->m_DirectCurrentTransform->GetTranslation()[1]); this->slideTransZ->setValue(this->m_DirectCurrentTransform->GetTranslation()[2]); this->sbRotX->setValue(this->m_DirectCurrentTransform->GetAngleX()*(180 / boost::math::double_constants::pi)); this->sbRotY->setValue(this->m_DirectCurrentTransform->GetAngleY()*(180 / boost::math::double_constants::pi)); this->sbRotZ->setValue(this->m_DirectCurrentTransform->GetAngleZ()*(180 / boost::math::double_constants::pi)); this->slideRotX->setValue(this->m_DirectCurrentTransform->GetAngleX()*(180 / boost::math::double_constants::pi)); this->slideRotY->setValue(this->m_DirectCurrentTransform->GetAngleY()*(180 / boost::math::double_constants::pi)); this->slideRotZ->setValue(this->m_DirectCurrentTransform->GetAngleZ()*(180 / boost::math::double_constants::pi)); this->m_internalUpdate = false; }; void QmitkRegistrationManipulationWidget::UpdateTransform(bool updateRotation) { if (updateRotation) { if (this->m_CenterOfRotationIsRelativeToTarget) { ConfigureTransformCenter(); } this->m_DirectCurrentTransform->SetRotation(this->sbRotX->value()*(boost::math::double_constants::pi / 180), this->sbRotY->value()*(boost::math::double_constants::pi / 180), this->sbRotZ->value()*(boost::math::double_constants::pi / 180)); } else { TransformType::OutputVectorType trans; trans[0] = this->sbTransX->value(); trans[1] = this->sbTransY->value(); trans[2] = this->sbTransZ->value(); this->m_DirectCurrentTransform->SetTranslation(trans); } this->m_DirectCurrentTransform->GetInverse(this->m_InverseCurrentTransform); this->UpdateTransformWidgets(); emit RegistrationChanged(this->m_CurrentRegistration); }; map::core::RegistrationBase* QmitkRegistrationManipulationWidget::GetInterimRegistration() const { return this->m_CurrentRegistration.GetPointer(); }; map::core::RegistrationBase::Pointer QmitkRegistrationManipulationWidget::GenerateRegistration() const { MAPRegistrationType::Pointer newReg = MAPRegistrationType::New(); ::map::core::RegistrationManipulator manipulator(newReg); ::map::core::PreCachedRegistrationKernel<3, 3>::Pointer kernel = ::map::core::PreCachedRegistrationKernel<3, 3>::New(); kernel->setTransformModel(m_InverseCurrentTransform); ::map::core::PreCachedRegistrationKernel<3, 3>::Pointer kernel2 = ::map::core::PreCachedRegistrationKernel<3, 3>::New(); kernel2->setTransformModel(m_InverseCurrentTransform->GetInverseTransform()); manipulator.setInverseMapping(kernel); manipulator.setDirectMapping(kernel2); if (this->m_PreRegistration.IsNotNull()) { //compine registration with selected pre registration as baseline typedef ::map::core::RegistrationCombinator CombinatorType; CombinatorType::Pointer combinator = CombinatorType::New(); newReg = combinator->process(*m_PreRegistration, *newReg); } return newReg.GetPointer(); }; void QmitkRegistrationManipulationWidget::OnRotXChanged(double x) { if (!m_internalUpdate) { m_internalUpdate = true; this->slideRotX->setValue(x); m_internalUpdate = false; this->UpdateTransform(true); } }; void QmitkRegistrationManipulationWidget::OnRotXSlideChanged(int x) { if (!m_internalUpdate) { this->sbRotX->setValue(x); } }; void QmitkRegistrationManipulationWidget::OnRotYChanged(double y) { if (!m_internalUpdate) { m_internalUpdate = true; this->slideRotY->setValue(y); m_internalUpdate = false; this->UpdateTransform(true); } }; void QmitkRegistrationManipulationWidget::OnRotYSlideChanged(int y) { if (!m_internalUpdate) { this->sbRotY->setValue(y); } }; void QmitkRegistrationManipulationWidget::OnRotZChanged(double z) { if (!m_internalUpdate) { m_internalUpdate = true; this->slideRotZ->setValue(z); m_internalUpdate = false; this->UpdateTransform(true); } }; void QmitkRegistrationManipulationWidget::OnRotZSlideChanged(int z) { if (!m_internalUpdate) { this->sbRotZ->setValue(z); } }; void QmitkRegistrationManipulationWidget::OnTransXChanged(double x) { if (!m_internalUpdate) { m_internalUpdate = true; this->slideTransX->setValue(x); m_internalUpdate = false; this->UpdateTransform(); } }; void QmitkRegistrationManipulationWidget::OnTransXSlideChanged(int x) { if (!m_internalUpdate) { this->sbTransX->setValue(x); } }; void QmitkRegistrationManipulationWidget::OnTransYChanged(double y) { if (!m_internalUpdate) { m_internalUpdate = true; this->slideTransY->setValue(y); m_internalUpdate = false; this->UpdateTransform(); } }; void QmitkRegistrationManipulationWidget::OnTransYSlideChanged(int y) { if (!m_internalUpdate) { this->sbTransY->setValue(y); } }; void QmitkRegistrationManipulationWidget::OnTransZChanged(double z) { if (!m_internalUpdate) { m_internalUpdate = true; this->slideTransZ->setValue(z); m_internalUpdate = false; this->UpdateTransform(); } }; void QmitkRegistrationManipulationWidget::OnTransZSlideChanged(int z) { if (!m_internalUpdate) { this->sbTransZ->setValue(z); } }; void QmitkRegistrationManipulationWidget::ConfigureTransformCenter() { auto offset = m_DirectCurrentTransform->GetOffset(); if (this->m_CenterOfRotationIsRelativeToTarget) { auto newCenter = m_InverseCurrentTransform->TransformPoint(m_CenterOfRotation); m_DirectCurrentTransform->SetCenter(newCenter); } else { m_DirectCurrentTransform->SetCenter(m_CenterOfRotation); } m_DirectCurrentTransform->SetOffset(offset); m_DirectCurrentTransform->GetInverse(m_InverseCurrentTransform); }; diff --git a/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp b/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp index 41f5f03a67..e5bc2b557d 100644 --- a/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp +++ b/Modules/Multilabel/Testing/mitkLabelSetImageIOTest.cpp @@ -1,234 +1,235 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include std::string pathToImage; class mitkLabelSetImageIOTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkLabelSetImageIOTestSuite); MITK_TEST(TestReadWrite3DLabelSetImage); MITK_TEST(TestReadWrite3DplusTLabelSetImage); MITK_TEST(TestReadWrite3DplusTLabelSetImageWithArbitraryGeometry); MITK_TEST(TestReadWriteProperties); CPPUNIT_TEST_SUITE_END(); private: mitk::Image::Pointer regularImage; mitk::LabelSetImage::Pointer multilabelImage; public: void setUp() override { regularImage = mitk::Image::New(); } void tearDown() override { regularImage = nullptr; multilabelImage = nullptr; } void TestReadWrite3DLabelSetImage() { unsigned int dimensions[3] = {30, 20, 10}; regularImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(0); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label0); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); multilabelImage->AddLayer(newlayer); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetTestImage3D.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); auto loadedImage = mitk::IOUtil::Load(pathToImage); // This information is currently not serialized but also checked within the Equals function loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true)); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Error, read image has different UID", multilabelImage->GetUID(), loadedImage->GetUID()); itksys::SystemTools::RemoveFile(pathToImage); } void TestReadWrite3DplusTLabelSetImage() { unsigned int dimensions[4] = {30, 20, 15, 10}; regularImage->Initialize(mitk::MakeScalarPixelType(), 4, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(0); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label0); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); multilabelImage->AddLayer(newlayer); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetTestImage3DplusT.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); auto loadedImage = mitk::IOUtil::Load(pathToImage); // This information is currently not serialized but also checked within the Equals function loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true)); CPPUNIT_ASSERT_MESSAGE("Error reading time geometry of label set image", mitk::Equal(*(multilabelImage->GetTimeGeometry()), *(loadedImage->GetTimeGeometry()), 0.000000001, true)); itksys::SystemTools::RemoveFile(pathToImage); } void TestReadWrite3DplusTLabelSetImageWithArbitraryGeometry() { unsigned int dimensions[4] = { 30, 20, 10, 4 }; regularImage->Initialize(mitk::MakeScalarPixelType(), 4, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(0); mitk::Label::Pointer label1 = mitk::Label::New(); label1->SetName("Label1"); label1->SetValue(1); mitk::Label::Pointer label2 = mitk::Label::New(); label2->SetName("Label2"); label2->SetValue(200); newlayer->AddLabel(label0); newlayer->AddLabel(label1); newlayer->AddLabel(label2); newlayer->SetActiveLabel(200); multilabelImage->AddLayer(newlayer); auto geometry = multilabelImage->GetGeometry()->Clone(); auto refTimeGeometry = mitk::ArbitraryTimeGeometry::New(); refTimeGeometry->AppendNewTimeStep(geometry, 0., 0.5); refTimeGeometry->AppendNewTimeStep(geometry, 0.5, 1.); refTimeGeometry->AppendNewTimeStep(geometry, 1., 2.); refTimeGeometry->AppendNewTimeStep(geometry, 2., 5.5); multilabelImage->SetTimeGeometry(refTimeGeometry); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetTestImage3DplusTWithArbitraryTimeGeometry.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); auto loadedImage = mitk::IOUtil::Load(pathToImage); // This information is currently not serialized but also checked within the Equals function loadedImage->SetActiveLayer(multilabelImage->GetActiveLayer()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", loadedImage.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading label set image", mitk::Equal(*multilabelImage, *loadedImage, 0.0001, true)); CPPUNIT_ASSERT_MESSAGE("Error reading time geometry of label set image", mitk::Equal(*refTimeGeometry, *(loadedImage->GetTimeGeometry()), 0.000000001, true)); itksys::SystemTools::RemoveFile(pathToImage); } void TestReadWriteProperties() { unsigned int dimensions[3] = { 30, 20, 10 }; regularImage->Initialize(mitk::MakeScalarPixelType(), 3, dimensions); multilabelImage = mitk::LabelSetImage::New(); multilabelImage->Initialize(regularImage); mitk::LabelSet::Pointer newlayer = mitk::LabelSet::New(); newlayer->SetLayer(1); mitk::Label::Pointer label0 = mitk::Label::New(); label0->SetName("Background"); label0->SetValue(0); newlayer->AddLabel(label0); multilabelImage->AddLayer(newlayer); auto propPersistenceInfo = mitk::PropertyPersistenceInfo::New(); propPersistenceInfo->SetNameAndKey("my.cool.test.property", "my_cool_test_property"); mitk::CoreServicePointer propPersService(mitk::CoreServices::GetPropertyPersistence()); propPersService->AddInfo(propPersistenceInfo); multilabelImage->SetProperty("my.cool.test.property", mitk::StringProperty::New("test_content")); pathToImage = mitk::IOUtil::CreateTemporaryDirectory(); pathToImage.append("/LabelSetPropertiesTestImage.nrrd"); mitk::IOUtil::Save(multilabelImage, pathToImage); auto loadedImage = mitk::IOUtil::Load(pathToImage); auto loadedProp = loadedImage->GetProperty("my.cool.test.property"); CPPUNIT_ASSERT_MESSAGE("Error reading properties of label set image", loadedProp.IsNotNull()); CPPUNIT_ASSERT_MESSAGE("Error reading properties of label set image", loadedProp->GetValueAsString() == "test_content"); itksys::SystemTools::RemoveFile(pathToImage); } }; MITK_TEST_SUITE_REGISTRATION(mitkLabelSetImageIO) diff --git a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp index fb7bc8b6ca..0731005329 100644 --- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp +++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp @@ -1,707 +1,707 @@ /*============================================================================ 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 __mitkDICOMSegmentationIO__cpp #define __mitkDICOMSegmentationIO__cpp #include "mitkDICOMSegmentationIO.h" #include "mitkDICOMSegIOMimeTypes.h" #include "mitkDICOMSegmentationConstants.h" #include #include #include #include #include #include #include #include // itk #include // dcmqi #include // us #include #include namespace mitk { DICOMSegmentationIO::DICOMSegmentationIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), mitk::MitkDICOMSEGIOMimeTypes::DICOMSEG_MIMETYPE_NAME(), "DICOM Segmentation") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); this->AddDICOMTagsToService(); } void DICOMSegmentationIO::AddDICOMTagsToService() { IDICOMTagsOfInterest *toiService = GetDicomTagsOfInterestService(); if (toiService != nullptr) { toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_CATEGORY_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_TYPE_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENTED_PROPERTY_MODIFIER_SEQUENCE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()); toiService->AddTagOfInterest(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()); } } IFileIO::ConfidenceLevel DICOMSegmentationIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; // Check if the input file is a segmentation const LabelSetImage *input = static_cast(this->GetInput()); if (input) { if ((input->GetDimension() != 3)) { MITK_INFO << "DICOM segmentation writer is tested only with 3D images, sorry."; return Unsupported; } // Check if input file has dicom information for the referenced image (original DICOM image, e.g. CT) Still necessary, see write() mitk::StringLookupTableProperty::Pointer dicomFilesProp = dynamic_cast(input->GetProperty("referenceFiles").GetPointer()); if (dicomFilesProp.IsNotNull()) return Supported; } return Unsupported; } void DICOMSegmentationIO::Write() { ValidateOutputLocation(); mitk::LocaleSwitch localeSwitch("C"); LocalFile localFile(this); const std::string path = localFile.GetFileName(); auto input = dynamic_cast(this->GetInput()); if (input == nullptr) mitkThrow() << "Cannot write non-image data"; // Get DICOM information from referenced image vector> dcmDatasetsSourceImage; std::unique_ptr readFileFormat(new DcmFileFormat()); try { // TODO: Generate dcmdataset witk DICOM tags from property list; ATM the source are the filepaths from the // property list mitk::StringLookupTableProperty::Pointer filesProp = dynamic_cast(input->GetProperty("referenceFiles").GetPointer()); if (filesProp.IsNull()) { mitkThrow() << "No property with dicom file path."; return; } StringLookupTable filesLut = filesProp->GetValue(); const StringLookupTable::LookupTableType &lookUpTableMap = filesLut.GetLookupTable(); for (auto it : lookUpTableMap) { const char *fileName = (it.second).c_str(); if (readFileFormat->loadFile(fileName, EXS_Unknown).good()) { std::unique_ptr readDCMDataset(readFileFormat->getAndRemoveDataset()); dcmDatasetsSourceImage.push_back(std::move(readDCMDataset)); } } } catch (const std::exception &e) { MITK_ERROR << "An error occurred while getting the dicom informations: " << e.what() << endl; return; } // Iterate over all layers. For each a dcm file will be generated for (unsigned int layer = 0; layer < input->GetNumberOfLayers(); ++layer) { vector segmentations; try { // Hack: Remove the const attribute to switch between the layer images. Normally you could get the different // layer images by input->GetLayerImage(layer) mitk::LabelSetImage *mitkLayerImage = const_cast(input); mitkLayerImage->SetActiveLayer(layer); // Cast mitk layer image to itk ImageToItk::Pointer imageToItkFilter = ImageToItk::New(); imageToItkFilter->SetInput(mitkLayerImage); // Cast from original itk type to dcmqi input itk image type typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(imageToItkFilter->GetOutput()); castFilter->Update(); itkInternalImageType::Pointer itkLabelImage = castFilter->GetOutput(); itkLabelImage->DisconnectPipeline(); // Iterate over all labels. For each label a segmentation image will be created const LabelSet *labelSet = input->GetLabelSet(layer); auto labelIter = labelSet->IteratorConstBegin(); // Ignore background label ++labelIter; for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter) { // Thresold over the image with the given label value itk::ThresholdImageFilter::Pointer thresholdFilter = itk::ThresholdImageFilter::New(); thresholdFilter->SetInput(itkLabelImage); thresholdFilter->ThresholdOutside(labelIter->first, labelIter->first); thresholdFilter->SetOutsideValue(0); thresholdFilter->Update(); itkInternalImageType::Pointer segmentImage = thresholdFilter->GetOutput(); segmentImage->DisconnectPipeline(); segmentations.push_back(segmentImage); } } catch (const itk::ExceptionObject &e) { MITK_ERROR << e.GetDescription() << endl; return; } // Create segmentation meta information const std::string tmpMetaInfoFile = this->CreateMetaDataJsonFile(layer); MITK_INFO << "Writing image: " << path << std::endl; try { //TODO is there a better way? Interface expects a vector of raw pointer. vector rawVecDataset; for (const auto& dcmDataSet : dcmDatasetsSourceImage) rawVecDataset.push_back(dcmDataSet.get()); // Convert itk segmentation images to dicom image std::unique_ptr converter = std::make_unique(); - std::unique_ptr result(converter->itkimage2dcmSegmentation(rawVecDataset, segmentations, tmpMetaInfoFile)); + std::unique_ptr result(converter->itkimage2dcmSegmentation(rawVecDataset, segmentations, tmpMetaInfoFile, false)); // Write dicom file DcmFileFormat dcmFileFormat(result.get()); std::string filePath = path.substr(0, path.find_last_of(".")); // If there is more than one layer, we have to write more than 1 dicom file if (input->GetNumberOfLayers() != 1) filePath = filePath + std::to_string(layer) + ".dcm"; else filePath = filePath + ".dcm"; dcmFileFormat.saveFile(filePath.c_str(), EXS_LittleEndianExplicit); } catch (const std::exception &e) { MITK_ERROR << "An error occurred during writing the DICOM Seg: " << e.what() << endl; return; } } // Write a dcm file for the next layer } IFileIO::ConfidenceLevel DICOMSegmentationIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); DcmFileFormat dcmFileFormat; OFCondition status = dcmFileFormat.loadFile(fileName.c_str()); if (status.bad()) return Unsupported; OFString modality; if (dcmFileFormat.getDataset()->findAndGetOFString(DCM_Modality, modality).good()) { if (modality.compare("SEG") == 0) return Supported; else return Unsupported; } return Unsupported; } std::vector DICOMSegmentationIO::DoRead() { mitk::LocaleSwitch localeSwitch("C"); LabelSetImage::Pointer labelSetImage; std::vector result; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << std::endl; if (path.empty()) mitkThrow() << "Empty filename in mitk::ItkImageIO "; try { // Get the dcm data set from file path DcmFileFormat dcmFileFormat; OFCondition status = dcmFileFormat.loadFile(path.c_str()); if (status.bad()) mitkThrow() << "Can't read the input file!"; DcmDataset *dataSet = dcmFileFormat.getDataset(); if (dataSet == nullptr) mitkThrow() << "Can't read data from input file!"; //=============================== dcmqi part ==================================== // Read the DICOM SEG images (segItkImages) and DICOM tags (metaInfo) std::unique_ptr converter = std::make_unique(); pair, string> dcmqiOutput = converter->dcmSegmentation2itkimage(dataSet); map segItkImages = dcmqiOutput.first; dcmqi::JSONSegmentationMetaInformationHandler metaInfo(dcmqiOutput.second.c_str()); metaInfo.read(); MITK_INFO << "Input " << metaInfo.getJSONOutputAsString(); //=============================================================================== // Get the label information from segment attributes for each itk image vector>::const_iterator segmentIter = metaInfo.segmentsAttributesMappingList.begin(); // For each itk image add a layer to the LabelSetImage output for (auto &element : segItkImages) { // Get the labeled image and cast it to mitkImage typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(element.second); castFilter->Update(); Image::Pointer layerImage; CastToMitkImage(castFilter->GetOutput(), layerImage); // Get pixel value of the label itkInternalImageType::ValueType segValue = 1; typedef itk::ImageRegionIterator IteratorType; // Iterate over the image to find the pixel value of the label IteratorType iter(element.second, element.second->GetLargestPossibleRegion()); iter.GoToBegin(); while (!iter.IsAtEnd()) { itkInputImageType::PixelType value = iter.Get(); if (value != 0) { segValue = value; break; } ++iter; } // Get Segment information map map segmentMap = (*segmentIter); map::const_iterator segmentMapIter = (*segmentIter).begin(); dcmqi::SegmentAttributes *segmentAttribute = (*segmentMapIter).second; OFString labelName; if (segmentAttribute->getSegmentedPropertyTypeCodeSequence() != nullptr) { segmentAttribute->getSegmentedPropertyTypeCodeSequence()->getCodeMeaning(labelName); if (segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence() != nullptr) { OFString modifier; segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence()->getCodeMeaning(modifier); labelName.append(" (").append(modifier).append(")"); } } else { labelName = std::to_string(segmentAttribute->getLabelID()).c_str(); if (labelName.empty()) labelName = "Unnamed"; } float tmp[3] = { 0.0, 0.0, 0.0 }; if (segmentAttribute->getRecommendedDisplayRGBValue() != nullptr) { tmp[0] = segmentAttribute->getRecommendedDisplayRGBValue()[0] / 255.0; tmp[1] = segmentAttribute->getRecommendedDisplayRGBValue()[1] / 255.0; tmp[2] = segmentAttribute->getRecommendedDisplayRGBValue()[2] / 255.0; } Label *newLabel = nullptr; // If labelSetImage do not exists (first image) if (labelSetImage.IsNull()) { // Initialize the labelSetImage with the read image labelSetImage = LabelSetImage::New(); labelSetImage->InitializeByLabeledImage(layerImage); // Already a label was generated, so set the information to this newLabel = labelSetImage->GetActiveLabel(labelSetImage->GetActiveLayer()); newLabel->SetName(labelName.c_str()); newLabel->SetColor(Color(tmp)); newLabel->SetValue(segValue); } else { // Add a new layer to the labelSetImage. Background label is set automatically labelSetImage->AddLayer(layerImage); // Add new label newLabel = new Label; newLabel->SetName(labelName.c_str()); newLabel->SetColor(Color(tmp)); newLabel->SetValue(segValue); labelSetImage->GetLabelSet(labelSetImage->GetActiveLayer())->AddLabel(newLabel); } // Add some more label properties this->SetLabelProperties(newLabel, segmentAttribute); ++segmentIter; } labelSetImage->GetLabelSet()->SetAllLabelsVisible(true); // Add some general DICOM Segmentation properties mitk::IDICOMTagsOfInterest *toiSrv = GetDicomTagsOfInterestService(); auto tagsOfInterest = toiSrv->GetTagsOfInterest(); DICOMTagPathList tagsOfInterestList; for (const auto &tag : tagsOfInterest) { tagsOfInterestList.push_back(tag.first); } mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); scanner->SetInputFiles({ GetInputLocation() }); scanner->AddTagPaths(tagsOfInterestList); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); if (frames.empty()) { MITK_ERROR << "Error reading the DICOM Seg file" << std::endl; return result; } auto findings = ExtractPathsOfInterest(tagsOfInterestList, frames); SetProperties(labelSetImage, findings); // Set active layer to the first layer of the labelset image if (labelSetImage->GetNumberOfLayers() > 1 && labelSetImage->GetActiveLayer() != 0) labelSetImage->SetActiveLayer(0); } catch (const std::exception &e) { MITK_ERROR << "An error occurred while reading the DICOM Seg file: " << e.what(); return result; } catch (...) { MITK_ERROR << "An error occurred in dcmqi while reading the DICOM Seg file"; return result; } result.push_back(labelSetImage.GetPointer()); return result; } const std::string mitk::DICOMSegmentationIO::CreateMetaDataJsonFile(int layer) { const mitk::LabelSetImage *image = dynamic_cast(this->GetInput()); const std::string output; dcmqi::JSONSegmentationMetaInformationHandler handler; // 1. Metadata attributes that will be listed in the resulting DICOM SEG object std::string contentCreatorName; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0070, 0x0084).c_str(), contentCreatorName)) contentCreatorName = "MITK"; handler.setContentCreatorName(contentCreatorName); std::string clinicalTrailSeriesId; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0071).c_str(), clinicalTrailSeriesId)) clinicalTrailSeriesId = "Session 1"; handler.setClinicalTrialSeriesID(clinicalTrailSeriesId); std::string clinicalTrialTimePointID; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0050).c_str(), clinicalTrialTimePointID)) clinicalTrialTimePointID = "0"; handler.setClinicalTrialTimePointID(clinicalTrialTimePointID); std::string clinicalTrialCoordinatingCenterName = ""; if (!image->GetPropertyList()->GetStringProperty(GeneratePropertyNameForDICOMTag(0x0012, 0x0060).c_str(), clinicalTrialCoordinatingCenterName)) clinicalTrialCoordinatingCenterName = "Unknown"; handler.setClinicalTrialCoordinatingCenterName(clinicalTrialCoordinatingCenterName); std::string seriesDescription; if (!image->GetPropertyList()->GetStringProperty("name", seriesDescription)) seriesDescription = "MITK Segmentation"; handler.setSeriesDescription(seriesDescription); handler.setSeriesNumber("0" + std::to_string(layer)); handler.setInstanceNumber("1"); handler.setBodyPartExamined(""); const LabelSet *labelSet = image->GetLabelSet(layer); auto labelIter = labelSet->IteratorConstBegin(); // Ignore background label ++labelIter; for (; labelIter != labelSet->IteratorConstEnd(); ++labelIter) { const Label *label = labelIter->second; if (label != nullptr) { TemporoSpatialStringProperty *segmentNumberProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str())); TemporoSpatialStringProperty *segmentLabelProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str())); TemporoSpatialStringProperty *algorithmTypeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str())); TemporoSpatialStringProperty *segmentCategoryCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str())); TemporoSpatialStringProperty *segmentCategoryCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str())); TemporoSpatialStringProperty *segmentCategoryCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str())); TemporoSpatialStringProperty *segmentTypeCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str())); TemporoSpatialStringProperty *segmentTypeCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str())); TemporoSpatialStringProperty *segmentTypeCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str())); TemporoSpatialStringProperty *segmentModifierCodeValueProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str())); TemporoSpatialStringProperty *segmentModifierCodeSchemeProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str())); TemporoSpatialStringProperty *segmentModifierCodeMeaningProp = dynamic_cast(label->GetProperty( mitk::DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str())); dcmqi::SegmentAttributes *segmentAttribute = nullptr; if (segmentNumberProp->GetValue() == "") { MITK_ERROR << "Something went wrong with the label ID."; } else { int labelId = std::stoi(segmentNumberProp->GetValue()); segmentAttribute = handler.createAndGetNewSegment(labelId); } if (segmentAttribute != nullptr) { segmentAttribute->setSegmentDescription(segmentLabelProp->GetValueAsString()); segmentAttribute->setSegmentAlgorithmType(algorithmTypeProp->GetValueAsString()); segmentAttribute->setSegmentAlgorithmName("MITK Segmentation"); if (segmentCategoryCodeValueProp != nullptr && segmentCategoryCodeSchemeProp != nullptr && segmentCategoryCodeMeaningProp != nullptr) segmentAttribute->setSegmentedPropertyCategoryCodeSequence( segmentCategoryCodeValueProp->GetValueAsString(), segmentCategoryCodeSchemeProp->GetValueAsString(), segmentCategoryCodeMeaningProp->GetValueAsString()); else // some default values segmentAttribute->setSegmentedPropertyCategoryCodeSequence( "M-01000", "SRT", "Morphologically Altered Structure"); if (segmentTypeCodeValueProp != nullptr && segmentTypeCodeSchemeProp != nullptr && segmentTypeCodeMeaningProp != nullptr) { segmentAttribute->setSegmentedPropertyTypeCodeSequence(segmentTypeCodeValueProp->GetValueAsString(), segmentTypeCodeSchemeProp->GetValueAsString(), segmentTypeCodeMeaningProp->GetValueAsString()); handler.setBodyPartExamined(segmentTypeCodeMeaningProp->GetValueAsString()); } else { // some default values segmentAttribute->setSegmentedPropertyTypeCodeSequence("M-03000", "SRT", "Mass"); handler.setBodyPartExamined("Mass"); } if (segmentModifierCodeValueProp != nullptr && segmentModifierCodeSchemeProp != nullptr && segmentModifierCodeMeaningProp != nullptr) segmentAttribute->setSegmentedPropertyTypeModifierCodeSequence( segmentModifierCodeValueProp->GetValueAsString(), segmentModifierCodeSchemeProp->GetValueAsString(), segmentModifierCodeMeaningProp->GetValueAsString()); Color color = label->GetColor(); segmentAttribute->setRecommendedDisplayRGBValue(color[0] * 255, color[1] * 255, color[2] * 255); } } } return handler.getJSONOutputAsString(); } void mitk::DICOMSegmentationIO::SetLabelProperties(mitk::Label *label, dcmqi::SegmentAttributes *segmentAttribute) { // Segment Number:Identification number of the segment.The value of Segment Number(0062, 0004) shall be unique // within the Segmentation instance in which it is created label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_NUMBER_PATH()).c_str(), TemporoSpatialStringProperty::New(std::to_string(label->GetValue()))); // Segment Label: User-defined label identifying this segment. label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_LABEL_PATH()).c_str(), TemporoSpatialStringProperty::New(label->GetName())); // Segment Algorithm Type: Type of algorithm used to generate the segment. if (!segmentAttribute->getSegmentAlgorithmType().empty()) label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_ALGORITHM_TYPE_PATH()).c_str(), TemporoSpatialStringProperty::New(segmentAttribute->getSegmentAlgorithmType())); // Add Segmented Property Category Code Sequence tags auto categoryCodeSequence = segmentAttribute->getSegmentedPropertyCategoryCodeSequence(); if (categoryCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value categoryCodeSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator categoryCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning categoryCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_CATEGORY_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Segmented Property Type Code Sequence tags auto typeCodeSequence = segmentAttribute->getSegmentedPropertyTypeCodeSequence(); if (typeCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value typeCodeSequence->getCodeValue(codeValue); label->SetProperty(DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator typeCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning typeCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_TYPE_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Segmented Property Type Modifier Code Sequence tags auto modifierCodeSequence = segmentAttribute->getSegmentedPropertyTypeModifierCodeSequence(); if (modifierCodeSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value modifierCodeSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator modifierCodeSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning modifierCodeSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::SEGMENT_MODIFIER_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } // Add Atomic RegionSequence tags auto atomicRegionSequence = segmentAttribute->getAnatomicRegionSequence(); if (atomicRegionSequence != nullptr) { OFString codeValue; // (0008,0100) Code Value atomicRegionSequence->getCodeValue(codeValue); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_VALUE_PATH()).c_str(), TemporoSpatialStringProperty::New(codeValue.c_str())); OFString codeScheme; // (0008,0102) Coding Scheme Designator atomicRegionSequence->getCodingSchemeDesignator(codeScheme); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_SCHEME_PATH()).c_str(), TemporoSpatialStringProperty::New(codeScheme.c_str())); OFString codeMeaning; // (0008,0104) Code Meaning atomicRegionSequence->getCodeMeaning(codeMeaning); label->SetProperty( DICOMTagPathToPropertyName(DICOMSegmentationConstants::ANATOMIC_REGION_CODE_MEANING_PATH()).c_str(), TemporoSpatialStringProperty::New(codeMeaning.c_str())); } } DICOMSegmentationIO *DICOMSegmentationIO::IOClone() const { return new DICOMSegmentationIO(*this); } } // namespace #endif //__mitkDICOMSegmentationIO__cpp diff --git a/Modules/Multilabel/autoload/IO/CMakeLists.txt b/Modules/Multilabel/autoload/IO/CMakeLists.txt index 9c62019a54..9a0856eff7 100644 --- a/Modules/Multilabel/autoload/IO/CMakeLists.txt +++ b/Modules/Multilabel/autoload/IO/CMakeLists.txt @@ -1,8 +1,6 @@ MITK_CREATE_MODULE( MultilabelIO - INCLUDE_DIRS - PRIVATE src/IO DEPENDS PUBLIC MitkMultilabel MitkSceneSerialization PACKAGE_DEPENDS PRIVATE ITK|ITKQuadEdgeMesh+ITKAntiAlias+ITKIONRRD AUTOLOAD_WITH MitkCore ) diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index 9466f4a70e..0e041818c9 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp @@ -1,636 +1,652 @@ /*============================================================================ 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 __mitkLabelSetImageWriter__cpp #define __mitkLabelSetImageWriter__cpp #include "mitkLabelSetImageIO.h" #include "mitkBasePropertySerializer.h" #include "mitkIOMimeTypes.h" #include "mitkImageAccessByItk.h" #include "mitkLabelSetIOHelper.h" #include "mitkLabelSetImageConverter.h" #include #include #include #include #include +#include // itk #include "itkImageFileReader.h" #include "itkImageFileWriter.h" #include "itkMetaDataDictionary.h" #include "itkMetaDataObject.h" #include "itkNrrdImageIO.h" namespace mitk { const char* const PROPERTY_NAME_TIMEGEOMETRY_TYPE = "org.mitk.timegeometry.type"; const char* const PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS = "org.mitk.timegeometry.timepoints"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TYPE = "org_mitk_timegeometry_type"; const char* const PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS = "org_mitk_timegeometry_timepoints"; + const char* const PROPERTY_KEY_UID = "org_mitk_uid"; LabelSetImageIO::LabelSetImageIO() : AbstractFileIO(LabelSetImage::GetStaticNameOfClass(), IOMimeTypes::NRRD_MIMETYPE(), "MITK Multilabel Image") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel LabelSetImageIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const auto *input = static_cast(this->GetInput()); if (input) return Supported; else return Unsupported; } void LabelSetImageIO::Write() { ValidateOutputLocation(); auto input = dynamic_cast(this->GetInput()); mitk::LocaleSwitch localeSwitch("C"); mitk::Image::Pointer inputVector = mitk::ConvertLabelSetImageToImage(input); // image write if (inputVector.IsNull()) { mitkThrow() << "Cannot write non-image data"; } itk::NrrdImageIO::Pointer nrrdImageIo = itk::NrrdImageIO::New(); // Clone the image geometry, because we might have to change it // for writing purposes BaseGeometry::Pointer geometry = inputVector->GetGeometry()->Clone(); // Check if geometry information will be lost if (inputVector->GetDimension() == 2 && !geometry->Is2DConvertable()) { MITK_WARN << "Saving a 2D image with 3D geometry information. Geometry information will be lost! You might " "consider using Convert2Dto3DImageFilter before saving."; // set matrix to identity mitk::AffineTransform3D::Pointer affTrans = mitk::AffineTransform3D::New(); affTrans->SetIdentity(); mitk::Vector3D spacing = geometry->GetSpacing(); mitk::Point3D origin = geometry->GetOrigin(); geometry->SetIndexToWorldTransform(affTrans); geometry->SetSpacing(spacing); geometry->SetOrigin(origin); } LocalFile localFile(this); const std::string path = localFile.GetFileName(); MITK_INFO << "Writing image: " << path << std::endl; try { // Implementation of writer using itkImageIO directly. This skips the use // of templated itkImageFileWriter, which saves the multiplexing on MITK side. const unsigned int dimension = inputVector->GetDimension(); const unsigned int *const dimensions = inputVector->GetDimensions(); const mitk::PixelType pixelType = inputVector->GetPixelType(); const mitk::Vector3D mitkSpacing = geometry->GetSpacing(); const mitk::Point3D mitkOrigin = geometry->GetOrigin(); // Due to templating in itk, we are forced to save a 4D spacing and 4D Origin, // though they are not supported in MITK itk::Vector spacing4D; spacing4D[0] = mitkSpacing[0]; spacing4D[1] = mitkSpacing[1]; spacing4D[2] = mitkSpacing[2]; spacing4D[3] = 1; // There is no support for a 4D spacing. However, we should have a valid value here itk::Vector origin4D; origin4D[0] = mitkOrigin[0]; origin4D[1] = mitkOrigin[1]; origin4D[2] = mitkOrigin[2]; origin4D[3] = 0; // There is no support for a 4D origin. However, we should have a valid value here // Set the necessary information for imageIO nrrdImageIo->SetNumberOfDimensions(dimension); nrrdImageIo->SetPixelType(pixelType.GetPixelType()); nrrdImageIo->SetComponentType(pixelType.GetComponentType() < PixelComponentUserType ? static_cast(pixelType.GetComponentType()) : itk::ImageIOBase::UNKNOWNCOMPONENTTYPE); nrrdImageIo->SetNumberOfComponents(pixelType.GetNumberOfComponents()); itk::ImageIORegion ioRegion(dimension); for (unsigned int i = 0; i < dimension; i++) { nrrdImageIo->SetDimensions(i, dimensions[i]); nrrdImageIo->SetSpacing(i, spacing4D[i]); nrrdImageIo->SetOrigin(i, origin4D[i]); mitk::Vector3D mitkDirection; mitkDirection.SetVnlVector(geometry->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix().get_column(i)); itk::Vector direction4D; direction4D[0] = mitkDirection[0]; direction4D[1] = mitkDirection[1]; direction4D[2] = mitkDirection[2]; // MITK only supports a 3x3 direction matrix. Due to templating in itk, however, we must // save a 4x4 matrix for 4D images. in this case, add an homogneous component to the matrix. if (i == 3) { direction4D[3] = 1; // homogenous component } else { direction4D[3] = 0; } vnl_vector axisDirection(dimension); for (unsigned int j = 0; j < dimension; j++) { axisDirection[j] = direction4D[j] / spacing4D[i]; } nrrdImageIo->SetDirection(i, axisDirection); ioRegion.SetSize(i, inputVector->GetLargestPossibleRegion().GetSize(i)); ioRegion.SetIndex(i, inputVector->GetLargestPossibleRegion().GetIndex(i)); } // use compression if available nrrdImageIo->UseCompressionOn(); nrrdImageIo->SetIORegion(ioRegion); nrrdImageIo->SetFileName(path); // label set specific meta data char keybuffer[512]; char valbuffer[512]; sprintf(keybuffer, "modality"); sprintf(valbuffer, "org.mitk.image.multilabel"); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); sprintf(keybuffer, "layers"); sprintf(valbuffer, "%1d", input->GetNumberOfLayers()); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); for (unsigned int layerIdx = 0; layerIdx < input->GetNumberOfLayers(); layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); // layer idx sprintf(valbuffer, "%1u", input->GetNumberOfLabels(layerIdx)); // number of labels for the layer itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), std::string(valbuffer)); auto iter = input->GetLabelSet(layerIdx)->IteratorConstBegin(); unsigned int count(0); while (iter != input->GetLabelSet(layerIdx)->IteratorConstEnd()) { std::unique_ptr document; document.reset(new TiXmlDocument()); auto *decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... document->LinkEndChild(decl); TiXmlElement *labelElem = mitk::LabelSetIOHelper::GetLabelAsTiXmlElement(iter->second); document->LinkEndChild(labelElem); TiXmlPrinter printer; printer.SetIndent(""); printer.SetLineBreak(""); document->Accept(&printer); sprintf(keybuffer, "org.mitk.label_%03u_%05u", layerIdx, count); itk::EncapsulateMetaData( nrrdImageIo->GetMetaDataDictionary(), std::string(keybuffer), printer.Str()); ++iter; ++count; } } // end label set specific meta data // Handle time geometry const auto* arbitraryTG = dynamic_cast(input->GetTimeGeometry()); if (arbitraryTG) { itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_TIMEGEOMETRY_TYPE, ArbitraryTimeGeometry::GetStaticNameOfClass()); auto metaTimePoints = ConvertTimePointListToMetaDataObject(arbitraryTG); nrrdImageIo->GetMetaDataDictionary().Set(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS, metaTimePoints); } // Handle properties mitk::PropertyList::Pointer imagePropertyList = input->GetPropertyList(); for (const auto& property : *imagePropertyList->GetMap()) { mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfo(property.first, GetMimeType()->GetName(), true); if (infoList.empty()) { continue; } std::string value = infoList.front()->GetSerializationFunction()(property.second); if (value == mitk::BaseProperty::VALUE_CANNOT_BE_CONVERTED_TO_STRING) { continue; } std::string key = infoList.front()->GetKey(); itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), key, value); } + // Handle UID + itk::EncapsulateMetaData(nrrdImageIo->GetMetaDataDictionary(), PROPERTY_KEY_UID, input->GetUID()); + ImageReadAccessor imageAccess(inputVector); nrrdImageIo->Write(imageAccess.GetData()); } catch (const std::exception &e) { mitkThrow() << e.what(); } // end image write } IFileIO::ConfidenceLevel LabelSetImageIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; const std::string fileName = this->GetLocalFileName(); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); io->SetFileName(fileName); io->ReadImageInformation(); itk::MetaDataDictionary imgMetaDataDictionary = io->GetMetaDataDictionary(); std::string value(""); itk::ExposeMetaData(imgMetaDataDictionary, "modality", value); if (value.compare("org.mitk.image.multilabel") == 0) { return Supported; } else return Unsupported; } std::vector LabelSetImageIO::DoRead() { mitk::LocaleSwitch localeSwitch("C"); // begin regular image loading, adapted from mitkItkImageIO itk::NrrdImageIO::Pointer nrrdImageIO = itk::NrrdImageIO::New(); Image::Pointer image = Image::New(); const unsigned int MINDIM = 2; const unsigned int MAXDIM = 4; const std::string path = this->GetLocalFileName(); MITK_INFO << "loading " << path << " via itk::ImageIOFactory... " << std::endl; // Check to see if we can read the file given the name or prefix if (path.empty()) { mitkThrow() << "Empty filename in mitk::ItkImageIO "; } // Got to allocate space for the image. Determine the characteristics of // the image. nrrdImageIO->SetFileName(path); nrrdImageIO->ReadImageInformation(); unsigned int ndim = nrrdImageIO->GetNumberOfDimensions(); if (ndim < MINDIM || ndim > MAXDIM) { MITK_WARN << "Sorry, only dimensions 2, 3 and 4 are supported. The given file has " << ndim << " dimensions! Reading as 4D."; ndim = MAXDIM; } itk::ImageIORegion ioRegion(ndim); itk::ImageIORegion::SizeType ioSize = ioRegion.GetSize(); itk::ImageIORegion::IndexType ioStart = ioRegion.GetIndex(); unsigned int dimensions[MAXDIM]; dimensions[0] = 0; dimensions[1] = 0; dimensions[2] = 0; dimensions[3] = 0; ScalarType spacing[MAXDIM]; spacing[0] = 1.0f; spacing[1] = 1.0f; spacing[2] = 1.0f; spacing[3] = 1.0f; Point3D origin; origin.Fill(0); unsigned int i; for (i = 0; i < ndim; ++i) { ioStart[i] = 0; ioSize[i] = nrrdImageIO->GetDimensions(i); if (i < MAXDIM) { dimensions[i] = nrrdImageIO->GetDimensions(i); spacing[i] = nrrdImageIO->GetSpacing(i); if (spacing[i] <= 0) spacing[i] = 1.0f; } if (i < 3) { origin[i] = nrrdImageIO->GetOrigin(i); } } ioRegion.SetSize(ioSize); ioRegion.SetIndex(ioStart); MITK_INFO << "ioRegion: " << ioRegion << std::endl; nrrdImageIO->SetIORegion(ioRegion); void *buffer = new unsigned char[nrrdImageIO->GetImageSizeInBytes()]; nrrdImageIO->Read(buffer); image->Initialize(MakePixelType(nrrdImageIO), ndim, dimensions); image->SetImportChannel(buffer, 0, Image::ManageMemory); // access direction of itk::Image and include spacing mitk::Matrix3D matrix; matrix.SetIdentity(); unsigned int j, itkDimMax3 = (ndim >= 3 ? 3 : ndim); for (i = 0; i < itkDimMax3; ++i) for (j = 0; j < itkDimMax3; ++j) matrix[i][j] = nrrdImageIO->GetDirection(j)[i]; // re-initialize PlaneGeometry with origin and direction PlaneGeometry *planeGeometry = image->GetSlicedGeometry(0)->GetPlaneGeometry(0); planeGeometry->SetOrigin(origin); planeGeometry->GetIndexToWorldTransform()->SetMatrix(matrix); // re-initialize SlicedGeometry3D SlicedGeometry3D *slicedGeometry = image->GetSlicedGeometry(0); slicedGeometry->InitializeEvenlySpaced(planeGeometry, image->GetDimension(2)); slicedGeometry->SetSpacing(spacing); MITK_INFO << slicedGeometry->GetCornerPoint(false, false, false); MITK_INFO << slicedGeometry->GetCornerPoint(true, true, true); // re-initialize TimeGeometry const itk::MetaDataDictionary& dictionary = nrrdImageIO->GetMetaDataDictionary(); TimeGeometry::Pointer timeGeometry; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE) || dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TYPE)) { // also check for the name because of backwards compatibility. Past code version stored with the name and not with // the key itk::MetaDataObject::ConstPointer timeGeometryTypeData; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TYPE)) { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TYPE)); } else { timeGeometryTypeData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TYPE)); } if (timeGeometryTypeData->GetMetaDataObjectValue() == ArbitraryTimeGeometry::GetStaticNameOfClass()) { MITK_INFO << "used time geometry: " << ArbitraryTimeGeometry::GetStaticNameOfClass(); typedef std::vector TimePointVector; TimePointVector timePoints; if (dictionary.HasKey(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS)); } else if (dictionary.HasKey(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)) { timePoints = ConvertMetaDataObjectToTimePointList(dictionary.Get(PROPERTY_KEY_TIMEGEOMETRY_TIMEPOINTS)); } if (timePoints.empty()) { MITK_ERROR << "Stored timepoints are empty. Meta information seems to bee invalid. Switch to ProportionalTimeGeometry fallback"; } else if (timePoints.size() - 1 != image->GetDimension(3)) { MITK_ERROR << "Stored timepoints (" << timePoints.size() - 1 << ") and size of image time dimension (" << image->GetDimension(3) << ") do not match. Switch to ProportionalTimeGeometry fallback"; } else { ArbitraryTimeGeometry::Pointer arbitraryTimeGeometry = ArbitraryTimeGeometry::New(); TimePointVector::const_iterator pos = timePoints.begin(); auto prePos = pos++; for (; pos != timePoints.end(); ++prePos, ++pos) { arbitraryTimeGeometry->AppendNewTimeStepClone(slicedGeometry, *prePos, *pos); } timeGeometry = arbitraryTimeGeometry; } } } if (timeGeometry.IsNull()) { // Fallback. If no other valid time geometry has been created, create a ProportionalTimeGeometry MITK_INFO << "used time geometry: " << ProportionalTimeGeometry::GetStaticNameOfClass(); ProportionalTimeGeometry::Pointer propTimeGeometry = ProportionalTimeGeometry::New(); propTimeGeometry->Initialize(slicedGeometry, image->GetDimension(3)); timeGeometry = propTimeGeometry; } image->SetTimeGeometry(timeGeometry); buffer = nullptr; MITK_INFO << "number of image components: " << image->GetPixelType().GetNumberOfComponents(); // end regular image loading LabelSetImage::Pointer output = ConvertImageToLabelSetImage(image); // get labels and add them as properties to the image char keybuffer[256]; unsigned int numberOfLayers = GetIntByKey(dictionary, "layers"); std::string _xmlStr; mitk::Label::Pointer label; for (unsigned int layerIdx = 0; layerIdx < numberOfLayers; layerIdx++) { sprintf(keybuffer, "layer_%03u", layerIdx); int numberOfLabels = GetIntByKey(dictionary, keybuffer); mitk::LabelSet::Pointer labelSet = mitk::LabelSet::New(); for (int labelIdx = 0; labelIdx < numberOfLabels; labelIdx++) { TiXmlDocument doc; sprintf(keybuffer, "label_%03u_%05d", layerIdx, labelIdx); _xmlStr = GetStringByKey(dictionary, keybuffer); doc.Parse(_xmlStr.c_str()); TiXmlElement *labelElem = doc.FirstChildElement("Label"); if (labelElem == nullptr) mitkThrow() << "Error parsing NRRD header for mitk::LabelSetImage IO"; label = mitk::LabelSetIOHelper::LoadLabelFromTiXmlDocument(labelElem); if (label->GetValue() == 0) // set exterior label is needed to hold exterior information output->SetExteriorLabel(label); labelSet->AddLabel(label); labelSet->SetLayer(layerIdx); } output->AddLabelSetToLayer(layerIdx, labelSet); } for (auto iter = dictionary.Begin(), iterEnd = dictionary.End(); iter != iterEnd; ++iter) { if (iter->second->GetMetaDataObjectTypeInfo() == typeid(std::string)) { const std::string& key = iter->first; std::string assumedPropertyName = key; std::replace(assumedPropertyName.begin(), assumedPropertyName.end(), '_', '.'); std::string mimeTypeName = GetMimeType()->GetName(); // Check if there is already a info for the key and our mime type. mitk::CoreServicePointer propPersistenceService(mitk::CoreServices::GetPropertyPersistence()); IPropertyPersistence::InfoResultType infoList = propPersistenceService->GetInfoByKey(key); auto predicate = [&mimeTypeName](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == mimeTypeName; }; auto finding = std::find_if(infoList.begin(), infoList.end(), predicate); if (finding == infoList.end()) { auto predicateWild = [](const PropertyPersistenceInfo::ConstPointer& x) { return x.IsNotNull() && x->GetMimeTypeName() == PropertyPersistenceInfo::ANY_MIMETYPE_NAME(); }; finding = std::find_if(infoList.begin(), infoList.end(), predicateWild); } PropertyPersistenceInfo::ConstPointer info; if (finding != infoList.end()) { assumedPropertyName = (*finding)->GetName(); info = *finding; } else { // we have not found anything suitable so we generate our own info auto newInfo = PropertyPersistenceInfo::New(); newInfo->SetNameAndKey(assumedPropertyName, key); newInfo->SetMimeTypeName(PropertyPersistenceInfo::ANY_MIMETYPE_NAME()); info = newInfo; } std::string value = dynamic_cast*>(iter->second.GetPointer())->GetMetaDataObjectValue(); mitk::BaseProperty::Pointer loadedProp = info->GetDeserializationFunction()(value); output->SetProperty(assumedPropertyName.c_str(), loadedProp); // Read properties should be persisted unless they are default properties // which are written anyway bool isDefaultKey = false; for (const auto& defaultKey : m_DefaultMetaDataKeys) { if (defaultKey.length() <= assumedPropertyName.length()) { // does the start match the default key if (assumedPropertyName.substr(0, defaultKey.length()).find(defaultKey) != std::string::npos) { isDefaultKey = true; break; } } } if (!isDefaultKey) { propPersistenceService->AddInfo(info); } } } + // Handle UID + if (dictionary.HasKey(PROPERTY_KEY_UID)) + { + itk::MetaDataObject::ConstPointer uidData = dynamic_cast*>(dictionary.Get(PROPERTY_KEY_UID)); + if (uidData.IsNotNull()) + { + mitk::UIDManipulator uidManipulator(output); + uidManipulator.SetUID(uidData->GetMetaDataObjectValue()); + } + } + MITK_INFO << "...finished!"; std::vector result; result.push_back(output.GetPointer()); return result; } int LabelSetImageIO::GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return atoi(metaString.c_str()); } } return 0; } std::string LabelSetImageIO::GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str) { std::vector imgMetaKeys = dic.GetKeys(); std::vector::const_iterator itKey = imgMetaKeys.begin(); std::string metaString(""); for (; itKey != imgMetaKeys.end(); itKey++) { itk::ExposeMetaData(dic, *itKey, metaString); if (itKey->find(str.c_str()) != std::string::npos) { return metaString; } } return metaString; } LabelSetImageIO *LabelSetImageIO::IOClone() const { return new LabelSetImageIO(*this); } void LabelSetImageIO::InitializeDefaultMetaDataKeys() { this->m_DefaultMetaDataKeys.push_back("NRRD.space"); this->m_DefaultMetaDataKeys.push_back("NRRD.kinds"); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TYPE); this->m_DefaultMetaDataKeys.push_back(PROPERTY_NAME_TIMEGEOMETRY_TIMEPOINTS); this->m_DefaultMetaDataKeys.push_back("ITK.InputFilterName"); this->m_DefaultMetaDataKeys.push_back("label_"); this->m_DefaultMetaDataKeys.push_back("layer_"); } } // namespace #endif //__mitkLabelSetImageWriter__cpp diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h index 4b079dc5e4..2c78d6d26c 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h @@ -1,68 +1,69 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkLabelSetImageIO_h #define __mitkLabelSetImageIO_h #include #include namespace mitk { /** - * Writes a LabelSetImage to a file + * Writes a LabelSetImage to a file. + * mitk::Identifiable UID is supported and will be serialized. * @ingroup Process */ // The export macro should be removed. Currently, the unit // tests directly instantiate this class. class LabelSetImageIO : public mitk::AbstractFileIO { public: typedef mitk::LabelSetImage InputType; LabelSetImageIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; // -------------- LabelSetImageIO specific functions ------------- int GetIntByKey(const itk::MetaDataDictionary &dic, const std::string &str); std::string GetStringByKey(const itk::MetaDataDictionary &dic, const std::string &str); protected: /** * @brief Reads a number of mitk::LabelSetImages from the file system * @return a vector of mitk::LabelSetImages * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header */ std::vector> DoRead() override; // Fills the m_DefaultMetaDataKeys vector with default values virtual void InitializeDefaultMetaDataKeys(); private: LabelSetImageIO *IOClone() const override; std::vector m_DefaultMetaDataKeys; }; } // end of namespace mitk #endif // __mitkLabelSetImageIO_h diff --git a/Modules/Multilabel/mitkMultilabelObjectFactory.cpp b/Modules/Multilabel/mitkMultilabelObjectFactory.cpp index e402aeff8b..6bce2dc1d8 100644 --- a/Modules/Multilabel/mitkMultilabelObjectFactory.cpp +++ b/Modules/Multilabel/mitkMultilabelObjectFactory.cpp @@ -1,124 +1,124 @@ /*============================================================================ 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 "mitkMultilabelObjectFactory.h" #include "mitkBaseRenderer.h" #include "mitkCoreObjectFactory.h" #include "mitkDataNode.h" #include "mitkProperties.h" #include #include #include #include mitk::MultilabelObjectFactory::MultilabelObjectFactory() : CoreObjectFactoryBase() { static bool alreadyDone = false; if (!alreadyDone) { MITK_DEBUG << "MultilabelObjectFactory c'tor" << std::endl; CreateFileExtensionsMap(); alreadyDone = true; } } mitk::MultilabelObjectFactory::~MultilabelObjectFactory() { } mitk::Mapper::Pointer mitk::MultilabelObjectFactory::CreateMapper(mitk::DataNode *node, MapperSlotId id) { mitk::Mapper::Pointer newMapper = nullptr; mitk::BaseData *data = node->GetData(); if (id == mitk::BaseRenderer::Standard2D) { if ((dynamic_cast(data) != nullptr)) { newMapper = mitk::LabelSetImageVtkMapper2D::New(); newMapper->SetDataNode(node); } } return newMapper; } void mitk::MultilabelObjectFactory::SetDefaultProperties(mitk::DataNode *node) { if (node == nullptr) return; if (node->GetData() == nullptr) return; if (dynamic_cast(node->GetData()) != nullptr) { mitk::LabelSetImageVtkMapper2D::SetDefaultProperties(node); auto propertyFilters = CoreServices::GetPropertyFilters(); if (propertyFilters != nullptr) { PropertyFilter labelSetImageFilter; labelSetImageFilter.AddEntry("binaryimage.hoveringannotationcolor", PropertyFilter::Blacklist); labelSetImageFilter.AddEntry("binaryimage.hoveringcolor", PropertyFilter::Blacklist); labelSetImageFilter.AddEntry("binaryimage.selectedannotationcolor", PropertyFilter::Blacklist); labelSetImageFilter.AddEntry("binaryimage.selectedcolor", PropertyFilter::Blacklist); labelSetImageFilter.AddEntry("outline binary shadow color", PropertyFilter::Blacklist); propertyFilters->AddFilter(labelSetImageFilter, "LabelSetImage"); } } } const char *mitk::MultilabelObjectFactory::GetFileExtensions() { std::string fileExtension; - this->CreateFileExtensions(m_FileExtensionsMap, fileExtension); + this->CreateFileExtensions({}, fileExtension); return fileExtension.c_str(); } mitk::CoreObjectFactoryBase::MultimapType mitk::MultilabelObjectFactory::GetFileExtensionsMap() { - return m_FileExtensionsMap; + return {}; } mitk::CoreObjectFactoryBase::MultimapType mitk::MultilabelObjectFactory::GetSaveFileExtensionsMap() { - return m_SaveFileExtensionsMap; + return {}; } void mitk::MultilabelObjectFactory::CreateFileExtensionsMap() { } const char *mitk::MultilabelObjectFactory::GetSaveFileExtensions() { std::string fileExtension; - this->CreateFileExtensions(m_SaveFileExtensionsMap, fileExtension); + this->CreateFileExtensions({}, fileExtension); return fileExtension.c_str(); } struct RegisterMultilabelObjectFactory { RegisterMultilabelObjectFactory() : m_Factory(mitk::MultilabelObjectFactory::New()) { mitk::CoreObjectFactory::GetInstance()->RegisterExtraFactory(m_Factory); } ~RegisterMultilabelObjectFactory() { mitk::CoreObjectFactory::GetInstance()->UnRegisterExtraFactory(m_Factory); } mitk::MultilabelObjectFactory::Pointer m_Factory; }; static RegisterMultilabelObjectFactory registerMultilabelObjectFactory; diff --git a/Modules/Multilabel/mitkMultilabelObjectFactory.h b/Modules/Multilabel/mitkMultilabelObjectFactory.h index 072f959e0e..2631cf6500 100644 --- a/Modules/Multilabel/mitkMultilabelObjectFactory.h +++ b/Modules/Multilabel/mitkMultilabelObjectFactory.h @@ -1,50 +1,42 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MULTILABELOBJECTFACTORY_H_INCLUDED #define MULTILABELOBJECTFACTORY_H_INCLUDED #include "mitkCoreObjectFactoryBase.h" #include namespace mitk { class MultilabelObjectFactory : public CoreObjectFactoryBase { public: mitkClassMacro(MultilabelObjectFactory, CoreObjectFactoryBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self) Mapper::Pointer CreateMapper(mitk::DataNode *node, MapperSlotId slotId) override; void SetDefaultProperties(mitk::DataNode *node) override; const char *GetFileExtensions() override; mitk::CoreObjectFactoryBase::MultimapType GetFileExtensionsMap() override; const char *GetSaveFileExtensions() override; mitk::CoreObjectFactoryBase::MultimapType GetSaveFileExtensionsMap() override; protected: MultilabelObjectFactory(); ~MultilabelObjectFactory() override; void CreateFileExtensionsMap(); - MultimapType m_FileExtensionsMap; - MultimapType m_SaveFileExtensionsMap; - - private: - itk::ObjectFactoryBase::Pointer m_LabelSetImageIOFactory; - itk::ObjectFactoryBase::Pointer m_LabelSetImageWriterFactory; - - std::vector m_FileIOs; }; } #endif diff --git a/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp b/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp index 04ffe4ae02..f6e48f7dcc 100644 --- a/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp +++ b/Modules/OpenIGTLink/mitkIGTLDeviceSource.cpp @@ -1,308 +1,308 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkIGTLDeviceSource.h" #include "mitkIGTLDevice.h" #include "mitkIGTLMessage.h" //#include "mitkIGTTimeStamp.h" //#include "mitkIGTException.h" //Microservices #include #include #include #include //itk #include const std::string mitk::IGTLDeviceSource::US_PROPKEY_IGTLDEVICENAME = mitk::IGTLMessageSource::US_INTERFACE_NAME + ".igtldevicename"; mitk::IGTLDeviceSource::IGTLDeviceSource() : mitk::IGTLMessageSource(), m_IGTLDevice(nullptr) { this->SetName("IGTLDeviceSource (no defined type)"); } mitk::IGTLDeviceSource::~IGTLDeviceSource() { if (m_IGTLDevice.IsNotNull()) { if (m_IGTLDevice->GetState() == mitk::IGTLDevice::Running) { this->StopCommunication(); } if (m_IGTLDevice->GetState() == mitk::IGTLDevice::Ready) { this->Disconnect(); } this->RemoveObservers(); m_IGTLDevice = nullptr; } } void mitk::IGTLDeviceSource::GenerateData() { if (m_IGTLDevice.IsNull()) return; /* update output with message from the device */ IGTLMessage* msgOut = this->GetOutput(); assert(msgOut); igtl::MessageBase::Pointer msgIn = dynamic_cast(m_IGTLDevice->GetNextImage2dMessage().GetPointer()); if (msgIn.IsNotNull()) { assert(msgIn); msgOut->SetMessage(msgIn); msgOut->SetName(msgIn->GetDeviceName()); } // else // { // MITK_ERROR("IGTLDeviceSource") << "Could not get the latest message."; // } } void mitk::IGTLDeviceSource::RemoveObservers() { if (this->m_IGTLDevice.IsNotNull()) { this->m_IGTLDevice->RemoveObserver(m_IncomingMessageObserverTag); this->m_IGTLDevice->RemoveObserver(m_IncomingCommandObserverTag); this->m_IGTLDevice->RemoveObserver(m_LostConnectionObserverTag); } } void mitk::IGTLDeviceSource::SetIGTLDevice(mitk::IGTLDevice* igtlDevice) { MITK_DEBUG << "Setting IGTLDevice to " << igtlDevice; if (this->m_IGTLDevice.GetPointer() != igtlDevice) { //check if we want to override the device if (this->m_IGTLDevice.IsNotNull()) { //the device was set previously => we need to reset the observers this->RemoveObservers(); } //set the device this->m_IGTLDevice = igtlDevice; this->CreateOutputs(); std::stringstream name; // create a human readable name for the source name << "OIGTL Device Source ( " << igtlDevice->GetName() << " )"; this->SetName(name.str()); //setup a observer that listens to new messages and new commands typedef itk::SimpleMemberCommand DeviceSrcCommand; DeviceSrcCommand::Pointer msgReceivedCommand = DeviceSrcCommand::New(); msgReceivedCommand->SetCallbackFunction(this, &IGTLDeviceSource::OnIncomingMessage); this->m_IncomingMessageObserverTag = this->m_IGTLDevice->AddObserver(mitk::MessageReceivedEvent(), msgReceivedCommand); DeviceSrcCommand::Pointer cmdReceivedCommand = DeviceSrcCommand::New(); cmdReceivedCommand->SetCallbackFunction(this, &IGTLDeviceSource::OnIncomingCommand); this->m_IncomingCommandObserverTag = this->m_IGTLDevice->AddObserver(mitk::CommandReceivedEvent(), cmdReceivedCommand); DeviceSrcCommand::Pointer connectionLostCommand = DeviceSrcCommand::New(); connectionLostCommand->SetCallbackFunction(this, &IGTLDeviceSource::OnLostConnection); this->m_LostConnectionObserverTag = this->m_IGTLDevice->AddObserver(mitk::LostConnectionEvent(), connectionLostCommand); } } void mitk::IGTLDeviceSource::CreateOutputs() { //if outputs are set then delete them if (this->GetNumberOfOutputs() > 0) { for (int numOP = this->GetNumberOfOutputs() - 1; numOP >= 0; numOP--) this->RemoveOutput(numOP); this->Modified(); } //fill the outputs if a valid OpenIGTLink device is set if (m_IGTLDevice.IsNull()) return; this->SetNumberOfIndexedOutputs(1); if (this->GetOutput(0) == nullptr) { DataObjectPointer newOutput = this->MakeOutput(0); this->SetNthOutput(0, newOutput); this->Modified(); } } void mitk::IGTLDeviceSource::Connect() { if (m_IGTLDevice.IsNull()) { throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); } if (this->IsConnected()) { return; } try { m_IGTLDevice->OpenConnection(); } catch (mitk::Exception &e) { throw std::runtime_error(std::string("mitk::IGTLDeviceSource: Could not open" "connection to OpenIGTLink device. Error: ") + e.GetDescription()); } } void mitk::IGTLDeviceSource::StartCommunication() { if (m_IGTLDevice.IsNull()) throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); if (m_IGTLDevice->GetState() == mitk::IGTLDevice::Running) return; if (m_IGTLDevice->StartCommunication() == false) throw std::runtime_error("mitk::IGTLDeviceSource: " "Could not start communication"); } void mitk::IGTLDeviceSource::Disconnect() { if (m_IGTLDevice.IsNull()) throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); if (m_IGTLDevice->CloseConnection() == false) throw std::runtime_error("mitk::IGTLDeviceSource: Could not close connection" " to OpenIGTLink device"); } void mitk::IGTLDeviceSource::StopCommunication() { if (m_IGTLDevice.IsNull()) throw std::invalid_argument("mitk::IGTLDeviceSource: " "No OpenIGTLink device set"); if (m_IGTLDevice->StopCommunication() == false) throw std::runtime_error("mitk::IGTLDeviceSource: " "Could not stop communicating"); } void mitk::IGTLDeviceSource::UpdateOutputInformation() { this->Modified(); // make sure that we need to be updated Superclass::UpdateOutputInformation(); } void mitk::IGTLDeviceSource::SetInput(unsigned int idx, const IGTLMessage* msg) { if (msg == nullptr) // if an input is set to nullptr, remove it { this->RemoveInput(idx); } else { // ProcessObject is not const-correct so a const_cast is required here this->ProcessObject::SetNthInput(idx, const_cast(msg)); } // this->CreateOutputsForAllInputs(); } bool mitk::IGTLDeviceSource::IsConnected() { if (m_IGTLDevice.IsNull()) return false; return (m_IGTLDevice->GetState() == mitk::IGTLDevice::Ready) || (m_IGTLDevice->GetState() == mitk::IGTLDevice::Running); } bool mitk::IGTLDeviceSource::IsCommunicating() { if (m_IGTLDevice.IsNull()) return false; return m_IGTLDevice->GetState() == mitk::IGTLDevice::Running; } void mitk::IGTLDeviceSource::RegisterAsMicroservice() { // Get Context us::ModuleContext* context = us::GetModuleContext(); // Define ServiceProps us::ServiceProperties props; mitk::UIDGenerator uidGen = - mitk::UIDGenerator("org.mitk.services.IGTLDeviceSource.id_", 16); + mitk::UIDGenerator("org.mitk.services.IGTLDeviceSource.id_"); props[US_PROPKEY_ID] = uidGen.GetUID(); props[US_PROPKEY_DEVICENAME] = this->GetName(); props[US_PROPKEY_IGTLDEVICENAME] = m_Name; props[US_PROPKEY_DEVICETYPE] = m_Type; m_ServiceRegistration = context->RegisterService(this, props); MITK_INFO << "Registered new DeviceSource as microservice: " << uidGen.GetUID(); } void mitk::IGTLDeviceSource::OnIncomingMessage() { } void mitk::IGTLDeviceSource::OnIncomingCommand() { } void mitk::IGTLDeviceSource::OnLostConnection() { } const mitk::IGTLMessage* mitk::IGTLDeviceSource::GetInput(void) const { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(0)); } const mitk::IGTLMessage* mitk::IGTLDeviceSource::GetInput(unsigned int idx) const { if (this->GetNumberOfInputs() < 1) return nullptr; return static_cast(this->ProcessObject::GetInput(idx)); } const mitk::IGTLMessage* mitk::IGTLDeviceSource::GetInput(std::string msgName) const { const DataObjectPointerArray& inputs = const_cast(this)->GetInputs(); for (DataObjectPointerArray::const_iterator it = inputs.begin(); it != inputs.end(); ++it) if (std::string(msgName) == (static_cast(it->GetPointer()))->GetName()) return static_cast(it->GetPointer()); return nullptr; } itk::ProcessObject::DataObjectPointerArraySizeType mitk::IGTLDeviceSource::GetInputIndex(std::string msgName) { DataObjectPointerArray outputs = this->GetInputs(); for (DataObjectPointerArray::size_type i = 0; i < outputs.size(); ++i) if (msgName == (static_cast(outputs.at(i).GetPointer()))->GetName()) return i; throw std::invalid_argument("output name does not exist"); } diff --git a/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp b/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp index 0e7ccfbfc0..f42dedd4dd 100644 --- a/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp +++ b/Modules/OpenIGTLink/mitkIGTLMessageSource.cpp @@ -1,198 +1,198 @@ /*============================================================================ 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 "mitkIGTLMessageSource.h" #include "mitkUIDGenerator.h" //Microservices #include #include #include #include const std::string mitk::IGTLMessageSource::US_INTERFACE_NAME = "org.mitk.services.IGTLMessageSource"; const std::string mitk::IGTLMessageSource::US_PROPKEY_DEVICENAME = US_INTERFACE_NAME + ".devicename"; const std::string mitk::IGTLMessageSource::US_PROPKEY_DEVICETYPE = US_INTERFACE_NAME + ".devicetype"; const std::string mitk::IGTLMessageSource::US_PROPKEY_ID = US_INTERFACE_NAME + ".id"; const std::string mitk::IGTLMessageSource::US_PROPKEY_ISACTIVE = US_INTERFACE_NAME + ".isActive"; mitk::IGTLMessageSource::IGTLMessageSource() : itk::ProcessObject(), m_Name("IGTLMessageSource (no defined type)"), m_Type("NONE"), m_StreamingFPS(0) { m_StreamingFPSMutex = itk::FastMutexLock::New(); } mitk::IGTLMessageSource::~IGTLMessageSource() { //this->UnRegisterMicroservice(); } mitk::IGTLMessage* mitk::IGTLMessageSource::GetOutput() { if (this->GetNumberOfIndexedOutputs() < 1) { MITK_WARN << "IGTLMessageSource contained no outputs. Returning nullptr."; return nullptr; } return static_cast(this->ProcessObject::GetPrimaryOutput()); } mitk::IGTLMessage* mitk::IGTLMessageSource::GetOutput( DataObjectPointerArraySizeType idx) { IGTLMessage* out = dynamic_cast( this->ProcessObject::GetOutput(idx) ); if ( out == nullptr && this->ProcessObject::GetOutput(idx) != nullptr ) { itkWarningMacro (<< "Unable to convert output number " << idx << " to type " << typeid( IGTLMessage ).name () ); } return out; } mitk::IGTLMessage* mitk::IGTLMessageSource::GetOutput( const std::string& messageName) { DataObjectPointerArray outputs = this->GetOutputs(); for (DataObjectPointerArray::iterator it = outputs.begin(); it != outputs.end(); ++it) { if (messageName == (static_cast(it->GetPointer()))->GetName()) { return static_cast(it->GetPointer()); } } return nullptr; } itk::ProcessObject::DataObjectPointerArraySizeType mitk::IGTLMessageSource::GetOutputIndex( std::string messageName ) { DataObjectPointerArray outputs = this->GetOutputs(); for (DataObjectPointerArray::size_type i = 0; i < outputs.size(); ++i) { if (messageName == (static_cast(outputs.at(i).GetPointer()))->GetName()) { return i; } } throw std::invalid_argument("output name does not exist"); } void mitk::IGTLMessageSource::RegisterAsMicroservice() { // Get Context us::ModuleContext* context = us::GetModuleContext(); // Define ServiceProps us::ServiceProperties props; mitk::UIDGenerator uidGen = - mitk::UIDGenerator ("org.mitk.services.IGTLMessageSource.id_", 16); + mitk::UIDGenerator ("org.mitk.services.IGTLMessageSource.id_"); props[ US_PROPKEY_ID ] = uidGen.GetUID(); props[ US_PROPKEY_DEVICENAME ] = m_Name; props[ US_PROPKEY_DEVICETYPE ] = m_Type; m_ServiceRegistration = context->RegisterService(this, props); } void mitk::IGTLMessageSource::UnRegisterMicroservice() { if (m_ServiceRegistration != nullptr) { m_ServiceRegistration.Unregister(); } m_ServiceRegistration = 0; } std::string mitk::IGTLMessageSource::GetMicroserviceID() { us::Any referenceProperty = this->m_ServiceRegistration.GetReference().GetProperty(US_PROPKEY_ID); return referenceProperty.ToString(); } void mitk::IGTLMessageSource::GraftOutput(itk::DataObject *graft) { this->GraftNthOutput(0, graft); } void mitk::IGTLMessageSource::GraftNthOutput(unsigned int idx, itk::DataObject *graft) { if ( idx >= this->GetNumberOfIndexedOutputs() ) { itkExceptionMacro(<<"Requested to graft output " << idx << " but this filter" "only has " << this->GetNumberOfIndexedOutputs() << " Outputs."); } if ( !graft ) { itkExceptionMacro(<<"Requested to graft output with a nullptr pointer object" ); } itk::DataObject* output = this->GetOutput(idx); if ( !output ) { itkExceptionMacro(<<"Requested to graft output that is a nullptr pointer" ); } // Call Graft on IGTLMessage to copy member data output->Graft( graft ); } itk::DataObject::Pointer mitk::IGTLMessageSource::MakeOutput ( DataObjectPointerArraySizeType /*idx*/ ) { return IGTLMessage::New().GetPointer(); } itk::DataObject::Pointer mitk::IGTLMessageSource::MakeOutput( const DataObjectIdentifierType & name ) { itkDebugMacro("MakeOutput(" << name << ")"); if( this->IsIndexedOutputName(name) ) { return this->MakeOutput( this->MakeIndexFromOutputName(name) ); } return static_cast(IGTLMessage::New().GetPointer()); } mitk::PropertyList::ConstPointer mitk::IGTLMessageSource::GetParameters() const { mitk::PropertyList::Pointer p = mitk::PropertyList::New(); // add properties to p like this: //p->SetProperty("MyFilter_MyParameter", mitk::PropertyDataType::New(m_MyParameter)); return mitk::PropertyList::ConstPointer(p); } void mitk::IGTLMessageSource::SetFPS(unsigned int fps) { this->m_StreamingFPSMutex->Lock(); this->m_StreamingFPS = fps; this->m_StreamingFPSMutex->Unlock(); } unsigned int mitk::IGTLMessageSource::GetFPS() { unsigned int fps = 0; this->m_StreamingFPSMutex->Lock(); fps = this->m_StreamingFPS; this->m_StreamingFPSMutex->Unlock(); return fps; } diff --git a/Modules/Pharmacokinetics/autoload/Models/mitkPharmacokineticModelsActivator.cpp b/Modules/Pharmacokinetics/autoload/Models/mitkPharmacokineticModelsActivator.cpp index 26ea2d0521..d67c683c15 100644 --- a/Modules/Pharmacokinetics/autoload/Models/mitkPharmacokineticModelsActivator.cpp +++ b/Modules/Pharmacokinetics/autoload/Models/mitkPharmacokineticModelsActivator.cpp @@ -1,80 +1,82 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include //MR perfusion models #include "mitkDescriptivePharmacokineticBrixModelFactory.h" #include "mitkExtendedToftsModelFactory.h" #include "mitkStandardToftsModelFactory.h" #include "mitkTwoCompartmentExchangeModelFactory.h" #include "mitkNumericTwoCompartmentExchangeModelFactory.h" //PET perfusion models #include "mitkOneTissueCompartmentModelFactory.h" #include "mitkExtendedOneTissueCompartmentModelFactory.h" #include "mitkTwoTissueCompartmentModelFactory.h" #include "mitkTwoTissueCompartmentFDGModelFactory.h" #include "mitkNumericTwoTissueCompartmentModelFactory.h" //general models +#include "mitkTwoStepLinearModelFactory.h" #include "mitkThreeStepLinearModelFactory.h" namespace mitk { /* * This is the module activator for the IO aspects of the "pharmacokinetics" module. */ class PharmacokineticModelsActivator : public us::ModuleActivator { public: template void RegisterProvider(us::ModuleContext* context) { auto provider = new TProvider(); provider->RegisterService(context); m_RegisteredProviders.push_back(std::unique_ptr(provider)); } void Load(us::ModuleContext* context) override { m_RegisteredProviders.clear(); RegisterProvider >(context); RegisterProvider >(context); + RegisterProvider >(context); RegisterProvider >(context); RegisterProvider >(context); RegisterProvider >(context); RegisterProvider >(context); RegisterProvider >(context); RegisterProvider >(context); RegisterProvider >(context); RegisterProvider >(context); RegisterProvider >(context); } void Unload(us::ModuleContext* ) override { } private: std::vector > m_RegisteredProviders; }; } US_EXPORT_MODULE_ACTIVATOR(mitk::PharmacokineticModelsActivator) diff --git a/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp b/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp index 6ebefbef87..03af6417c2 100644 --- a/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp +++ b/Modules/Pharmacokinetics/cmdapps/MRPerfusionMiniApp.cpp @@ -1,874 +1,890 @@ /*============================================================================ 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. ============================================================================*/ // std includes #include // itk includes #include "itksys/SystemTools.hxx" // CTK includes #include "mitkCommandLineParser.h" // MITK includes #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include +#include +#include #include std::string inFilename; std::string outFileName; std::string maskFileName; std::string aifMaskFileName; std::string aifImageFileName; mitk::Image::Pointer image; mitk::Image::Pointer mask; mitk::Image::Pointer aifImage; mitk::Image::Pointer aifMask; bool useConstraints(false); bool verbose(false); bool roibased(false); bool preview(false); std::string modelName; float aifHematocritLevel(0); float brixInjectionTime(0); +const std::string MODEL_NAME_2SL = "2SL"; const std::string MODEL_NAME_3SL = "3SL"; const std::string MODEL_NAME_descriptive = "descriptive"; const std::string MODEL_NAME_tofts = "tofts"; const std::string MODEL_NAME_2CX = "2CX"; void onFitEvent(::itk::Object* caller, const itk::EventObject & event, void* /*data*/) { itk::ProgressEvent progressEvent; if (progressEvent.CheckEvent(&event)) { mitk::ParameterFitImageGeneratorBase* castedReporter = dynamic_cast(caller); std::cout <GetProgress()*100 << "% "; } } void setupParser(mitkCommandLineParser& parser) { // set general information about your MiniApp parser.setCategory("Dynamic Data Analysis Tools"); parser.setTitle("MR Perfusion"); parser.setDescription("MiniApp that allows to fit MRI perfusion models and generates the according parameter maps. IMPORTANT!!!: The app assumes that the input images (signal and AIF) are concentration images. If your images do not hold this assumption, convert the image date before using this app (e.g. by using the signal-to-concentration-converter mini app."); parser.setContributor("DKFZ MIC"); //! [create parser] //! [add arguments] // how should arguments be prefixed parser.setArgumentPrefix("--", "-"); // add each argument, unless specified otherwise each argument is optional // see mitkCommandLineParser::addArgument for more information parser.beginGroup("Model parameters"); parser.addArgument( "model", "l", mitkCommandLineParser::String, "Model function", "Model that should be used to fit the concentration signal. Options are: \""+MODEL_NAME_descriptive+"\" (descriptive pharmacokinetic Brix model),\""+MODEL_NAME_3SL+"\" (three step linear model), \""+MODEL_NAME_tofts+"\" (extended tofts model) or \""+MODEL_NAME_2CX+"\" (two compartment exchange model).", us::Any(std::string(MODEL_NAME_tofts))); parser.addArgument( "injectiontime", "j", mitkCommandLineParser::Float, "Injection time [min]", "Injection time of the bolus. This information is needed for the descriptive pharmacokinetic Brix model.", us::Any()); parser.endGroup(); parser.beginGroup("Required I/O parameters"); parser.addArgument( "input", "i", mitkCommandLineParser::File, "Input file", "input 3D+t image file", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument("output", "o", mitkCommandLineParser::File, "Output file template", "where to save the output parameter images. The specified path will be used as template to determine the format (via extension) and the name \"root\". For each parameter a suffix will be added to the name.", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.endGroup(); parser.beginGroup("AIF parameters"); parser.addArgument( "aifmask", "n", mitkCommandLineParser::File, "AIF mask file", "Mask that defines the spatial image region that should be used as AIF for models that need one. Must have the same geometry as the AIF input image!", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument( "aifimage", "a", mitkCommandLineParser::File, "AIF image file", "3D+t image that defines the image that containes the AIF signal. If this flag is not set and the model needs a AIF, the CLI will assume that the AIF is encoded in the normal image. Must have the same geometry as the AIF mask!", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument( "hematocrit", "h", mitkCommandLineParser::Float, "Hematocrit Level", "Value needed for correct AIF computation. Only needed if model needs an AIF. Default value is 0.45.", us::Any(0.45)); parser.endGroup(); parser.beginGroup("Optional parameters"); parser.addArgument( "mask", "m", mitkCommandLineParser::File, "Mask file", "Mask that defines the spatial image region that should be fitted. Must have the same geometry as the input image!", us::Any(), true, false, false, mitkCommandLineParser::Input); parser.addArgument( "verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose output"); parser.addArgument( "roibased", "r", mitkCommandLineParser::Bool, "Roi based fitting", "Will compute a mean intesity signal over the ROI before fitting it. If this mode is used a mask must be specified."); parser.addArgument( "constraints", "c", mitkCommandLineParser::Bool, "Constraints", "Indicates if constraints should be used for the fitting (if flag is set the default contraints will be used.).", us::Any(false)); parser.addArgument( "preview", "p", mitkCommandLineParser::Bool, "Preview outputs", "The application previews the outputs (filename, type) it would produce with the current settings."); parser.addArgument("help", "h", mitkCommandLineParser::Bool, "Help:", "Show this help text"); parser.endGroup(); //! [add arguments] } bool configureApplicationSettings(std::map parsedArgs) { if (parsedArgs.size() == 0) return false; // parse, cast and set required arguments modelName = MODEL_NAME_tofts; if (parsedArgs.count("model")) { modelName = us::any_cast(parsedArgs["model"]); } inFilename = us::any_cast(parsedArgs["input"]); outFileName = us::any_cast(parsedArgs["output"]); if (parsedArgs.count("mask")) { maskFileName = us::any_cast(parsedArgs["mask"]); } if (parsedArgs.count("aifimage")) { aifImageFileName = us::any_cast(parsedArgs["aifimage"]); } if (parsedArgs.count("aifmask")) { aifMaskFileName = us::any_cast(parsedArgs["aifmask"]); } verbose = false; if (parsedArgs.count("verbose")) { verbose = us::any_cast(parsedArgs["verbose"]); } preview = false; if (parsedArgs.count("preview")) { preview = us::any_cast(parsedArgs["preview"]); } roibased = false; if (parsedArgs.count("roibased")) { roibased = us::any_cast(parsedArgs["roibased"]); } useConstraints = false; if (parsedArgs.count("constraints")) { useConstraints = us::any_cast(parsedArgs["constraints"]); } aifHematocritLevel = 0.45; if (parsedArgs.count("hematocrit")) { aifHematocritLevel = us::any_cast(parsedArgs["hematocrit"]); } brixInjectionTime = 0.0; if (parsedArgs.count("injectiontime")) { brixInjectionTime = us::any_cast(parsedArgs["injectiontime"]); } return true; } mitk::ModelFitFunctorBase::Pointer createDefaultFitFunctor( const mitk::ModelParameterizerBase* parameterizer, const mitk::ModelFactoryBase* modelFactory) { mitk::LevenbergMarquardtModelFitFunctor::Pointer fitFunctor = mitk::LevenbergMarquardtModelFitFunctor::New(); mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::Pointer chi2 = mitk::NormalizedSumOfSquaredDifferencesFitCostFunction::New(); fitFunctor->RegisterEvaluationParameter("Chi^2", chi2); if (useConstraints) { fitFunctor->SetConstraintChecker(modelFactory->CreateDefaultConstraints().GetPointer()); } mitk::ModelBase::Pointer refModel = parameterizer->GenerateParameterizedModel(); ::itk::LevenbergMarquardtOptimizer::ScalesType scales; scales.SetSize(refModel->GetNumberOfParameters()); scales.Fill(1.0); fitFunctor->SetScales(scales); fitFunctor->SetDebugParameterMaps(true); return fitFunctor.GetPointer(); } /**Helper that ensures that the mask (if it exists) is always 3D image. If the mask is originally an 4D image, the first time step will be used.*/ mitk::Image::Pointer getMask3D() { mitk::Image::Pointer result; if (mask.IsNotNull()) { result = mask; //mask settings if (mask->GetTimeSteps() > 1) { MITK_INFO << "Selected mask has multiple timesteps. Only use first timestep to mask model fit."; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(mask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); result = maskedImageTimeSelector->GetOutput(); } } return result; } void getAIF(mitk::AIFBasedModelBase::AterialInputFunctionType& aif, mitk::AIFBasedModelBase::AterialInputFunctionType& aifTimeGrid) { if (aifMask.IsNotNull()) { aif.clear(); aifTimeGrid.clear(); mitk::AterialInputFunctionGenerator::Pointer aifGenerator = mitk::AterialInputFunctionGenerator::New(); //Hematocrit level aifGenerator->SetHCL(aifHematocritLevel); std::cout << "AIF hematocrit level: " << aifHematocritLevel << std::endl; mitk::Image::Pointer selectedAIFMask = aifMask; //mask settings if (aifMask->GetTimeSteps() > 1) { MITK_INFO << "Selected AIF mask has multiple timesteps. Only use first timestep to mask model fit."; mitk::ImageTimeSelector::Pointer maskedImageTimeSelector = mitk::ImageTimeSelector::New(); maskedImageTimeSelector->SetInput(aifMask); maskedImageTimeSelector->SetTimeNr(0); maskedImageTimeSelector->UpdateLargestPossibleRegion(); aifMask = maskedImageTimeSelector->GetOutput(); } aifGenerator->SetMask(aifMask); mitk::Image::Pointer selectedAIFImage = image; //image settings if (aifImage.IsNotNull()) { selectedAIFImage = aifImage; } aifGenerator->SetDynamicImage(selectedAIFImage); aif = aifGenerator->GetAterialInputFunction(); aifTimeGrid = aifGenerator->GetAterialInputFunctionTimeGrid(); } else { mitkThrow() << "Cannot generate AIF. AIF mask was not specified or correctly loaded."; } } void generateDescriptiveBrixModel_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelParameterizer::New(); mitk::Image::Pointer mask3D = getMask3D(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(brixInjectionTime); std::cout << "Injection time [min]: " << brixInjectionTime << std::endl; mitk::ImageTimeSelector::Pointer imageTimeSelector = mitk::ImageTimeSelector::New(); imageTimeSelector->SetInput(image); imageTimeSelector->SetTimeNr(0); imageTimeSelector->UpdateLargestPossibleRegion(); mitk::DescriptivePharmacokineticBrixModelParameterizer::BaseImageType::Pointer baseImage; mitk::CastToItkImage(imageTimeSelector->GetOutput(), baseImage); modelParameterizer->SetBaseImage(baseImage); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (mask3D.IsNotNull()) { fitGenerator->SetMask(mask3D); roiUID = mitk::EnsureModelFitUID(mask); } fitGenerator->SetDynamicImage(image); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID); } void generateDescriptiveBrixModel_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::Image::Pointer mask3D = getMask3D(); if (mask3D.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::Pointer modelParameterizer = mitk::DescriptivePharmacokineticBrixModelValueBasedParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(mask3D); signalGenerator->SetDynamicImage(image); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Model configuration (static parameters) can be done now modelParameterizer->SetTau(brixInjectionTime); std::cout << "Injection time [min]: " << brixInjectionTime << std::endl; modelParameterizer->SetBaseValue(roiSignal[0]); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = mitk::DescriptivePharmacokineticBrixModelFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(mask3D); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image)); generator = fitGenerator.GetPointer(); std::string roiUID = mitk::EnsureModelFitUID(mask); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } - -void Generate3StepLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& +template +void GenerateLinearModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); - mitk::ThreeStepLinearModelParameterizer::Pointer modelParameterizer = - mitk::ThreeStepLinearModelParameterizer::New(); + typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); mitk::Image::Pointer mask3D = getMask3D(); //Specify fitting strategy and criterion parameters - mitk::ModelFactoryBase::Pointer factory = mitk::ThreeStepLinearModelFactory::New().GetPointer(); + mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (mask3D.IsNotNull()) { fitGenerator->SetMask(mask3D); roiUID = mitk::EnsureModelFitUID(mask); } fitGenerator->SetDynamicImage(image); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID); } -void Generate3StepLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& +template +void GenerateLinearModelFit_ROIBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::Image::Pointer mask3D = getMask3D(); if (mask3D.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); - mitk::ThreeStepLinearModelParameterizer::Pointer modelParameterizer = - mitk::ThreeStepLinearModelParameterizer::New(); + typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(mask3D); signalGenerator->SetDynamicImage(image); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Specify fitting strategy and criterion parameters - mitk::ModelFactoryBase::Pointer factory = mitk::ThreeStepLinearModelFactory::New().GetPointer(); + mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); + //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(mask3D); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image)); generator = fitGenerator.GetPointer(); std::string roiUID = mitk::EnsureModelFitUID(mask); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); } template void generateAIFbasedModelFit_PixelBased(mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::PixelBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::PixelBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; getAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); mitk::Image::Pointer mask3D = getMask3D(); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); std::string roiUID = ""; if (mask3D.IsNotNull()) { fitGenerator->SetMask(mask3D); roiUID = mitk::EnsureModelFitUID(mask); } fitGenerator->SetDynamicImage(image); fitGenerator->SetFitFunctor(fitFunctor); generator = fitGenerator.GetPointer(); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_PIXELBASED(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } template void generateAIFbasedModelFit_ROIBased( mitk::modelFit::ModelFitInfo::Pointer& modelFitInfo, mitk::ParameterFitImageGeneratorBase::Pointer& generator) { mitk::Image::Pointer mask3D = getMask3D(); if (mask3D.IsNull()) { return; } mitk::ROIBasedParameterFitImageGenerator::Pointer fitGenerator = mitk::ROIBasedParameterFitImageGenerator::New(); typename TParameterizer::Pointer modelParameterizer = TParameterizer::New(); mitk::AIFBasedModelBase::AterialInputFunctionType aif; mitk::AIFBasedModelBase::AterialInputFunctionType aifTimeGrid; getAIF(aif, aifTimeGrid); modelParameterizer->SetAIF(aif); modelParameterizer->SetAIFTimeGrid(aifTimeGrid); //Compute ROI signal mitk::MaskedDynamicImageStatisticsGenerator::Pointer signalGenerator = mitk::MaskedDynamicImageStatisticsGenerator::New(); signalGenerator->SetMask(mask3D); signalGenerator->SetDynamicImage(image); signalGenerator->Generate(); mitk::MaskedDynamicImageStatisticsGenerator::ResultType roiSignal = signalGenerator->GetMean(); //Specify fitting strategy and criterion parameters mitk::ModelFactoryBase::Pointer factory = TFactory::New().GetPointer(); mitk::ModelFitFunctorBase::Pointer fitFunctor = createDefaultFitFunctor(modelParameterizer, factory); //Parametrize fit generator fitGenerator->SetModelParameterizer(modelParameterizer); fitGenerator->SetMask(mask3D); fitGenerator->SetFitFunctor(fitFunctor); fitGenerator->SetSignal(roiSignal); fitGenerator->SetTimeGrid(mitk::ExtractTimeGrid(image)); generator = fitGenerator.GetPointer(); std::string roiUID = mitk::EnsureModelFitUID(mask); //Create model info modelFitInfo = mitk::modelFit::CreateFitInfoFromModelParameterizer(modelParameterizer, image, mitk::ModelFitConstants::FIT_TYPE_VALUE_ROIBASED(), roiUID); mitk::ScalarListLookupTable::ValueType infoSignal; for (mitk::MaskedDynamicImageStatisticsGenerator::ResultType::const_iterator pos = roiSignal.begin(); pos != roiSignal.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("ROI", infoSignal); infoSignal.clear(); for (mitk::AIFBasedModelBase::AterialInputFunctionType::const_iterator pos = aif.begin(); pos != aif.end(); ++pos) { infoSignal.push_back(*pos); } modelFitInfo->inputData.SetTableValue("AIF", infoSignal); } void storeResultImage(const std::string& name, mitk::Image* image, mitk::modelFit::Parameter::Type nodeType, const mitk::modelFit::ModelFitInfo* modelFitInfo) { mitk::modelFit::SetModelFitDataProperties(image, name, nodeType, modelFitInfo); std::string ext = ::itksys::SystemTools::GetFilenameLastExtension(outFileName); std::string dir = itksys::SystemTools::GetFilenamePath(outFileName); dir = itksys::SystemTools::ConvertToOutputPath(dir); std::string rootName = itksys::SystemTools::GetFilenameWithoutLastExtension(outFileName); std::string fileName = rootName + "_" + name + ext; std::vector pathElements; pathElements.push_back(dir); pathElements.push_back(fileName); std::string fullOutPath = itksys::SystemTools::ConvertToOutputPath(dir + "/" + fileName); mitk::IOUtil::Save(image, fullOutPath); std::cout << "Store result (parameter: "<(fitSession, generator); + } + else + { + GenerateLinearModelFit_ROIBased(fitSession, generator); + } + } + else if (is2SLFactory) + { + std::cout << "Model: two step linear model" << std::endl; + if (!roibased) + { + GenerateLinearModelFit_PixelBased(fitSession, generator); } else { - Generate3StepLinearModelFit_ROIBased(fitSession, generator); + GenerateLinearModelFit_ROIBased(fitSession, generator); } } else if (isToftsFactory) { std::cout << "Model: extended tofts model" << std::endl; if (!roibased) { generateAIFbasedModelFit_PixelBased(fitSession, generator); } else { generateAIFbasedModelFit_ROIBased(fitSession, generator); } } else if (is2CXMFactory) { std::cout << "Model: two compartment exchange model" << std::endl; if (!roibased) { generateAIFbasedModelFit_PixelBased(fitSession, generator); } else { generateAIFbasedModelFit_ROIBased(fitSession, generator); } } else { std::cerr << "ERROR. Model flag is unknown. Given flag: " << modelName << std::endl; } } void doFitting() { mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; ::itk::CStyleCommand::Pointer command = ::itk::CStyleCommand::New(); command->SetCallback(onFitEvent); createFitGenerator(fitSession, generator); if (generator.IsNotNull() ) { std::cout << "Started fitting process..." << std::endl; generator->AddObserver(::itk::AnyEvent(), command); generator->Generate(); std::cout << std::endl << "Finished fitting process" << std::endl; mitk::storeModelFitGeneratorResults(outFileName, generator, fitSession); } else { mitkThrow() << "Fitting error! Could not initialize fitting job."; } } void doPreview() { mitk::ParameterFitImageGeneratorBase::Pointer generator = nullptr; mitk::modelFit::ModelFitInfo::Pointer fitSession = nullptr; createFitGenerator(fitSession, generator); if (generator.IsNotNull()) { mitk::previewModelFitGeneratorResults(outFileName, generator); } else { mitkThrow() << "Fitting error! Could not initialize fitting job."; } } int main(int argc, char* argv[]) { mitkCommandLineParser parser; setupParser(parser); const std::map& parsedArgs = parser.parseArguments(argc, argv); mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (classic config)" }, { "MITK DICOM Reader" }); if (!configureApplicationSettings(parsedArgs)) { return EXIT_FAILURE; }; // Show a help message if (parsedArgs.count("help") || parsedArgs.count("h")) { std::cout << parser.helpText(); return EXIT_SUCCESS; } //! [do processing] try { image = mitk::IOUtil::Load(inFilename, &readerFilterFunctor); std::cout << "Input: " << inFilename << std::endl; if (!maskFileName.empty()) { mask = mitk::IOUtil::Load(maskFileName, &readerFilterFunctor); std::cout << "Mask: " << maskFileName << std::endl; } else { std::cout << "Mask: none" << std::endl; } if (modelName != MODEL_NAME_descriptive && modelName != MODEL_NAME_3SL) { if (!aifMaskFileName.empty()) { aifMask = mitk::IOUtil::Load(aifMaskFileName, &readerFilterFunctor); std::cout << "AIF mask: " << aifMaskFileName << std::endl; } else { mitkThrow() << "Error. Cannot fit. Choosen model needs an AIF. Please specify AIF mask (--aifmask)."; } if (!aifImageFileName.empty()) { aifImage = mitk::IOUtil::Load(aifImageFileName, &readerFilterFunctor); std::cout << "AIF image: " << aifImageFileName << std::endl; } else { std::cout << "AIF image: none (using signal image)" << std::endl; } } if (roibased && mask.IsNull()) { mitkThrow() << "Error. Cannot fit. Please specify mask if you select roi based fitting."; } std::cout << "Style: "; if (roibased) { std::cout << "ROI based"; } else { std::cout << "pixel based"; } std::cout << std::endl; if (preview) { doPreview(); } else { doFitting(); } std::cout << "Processing finished." << std::endl; return EXIT_SUCCESS; } catch (const itk::ExceptionObject& e) { MITK_ERROR << e.what(); return EXIT_FAILURE; } catch (const std::exception& e) { MITK_ERROR << e.what(); return EXIT_FAILURE; } catch (...) { MITK_ERROR << "Unexpected error encountered."; return EXIT_FAILURE; } } diff --git a/Modules/Pharmacokinetics/files.cmake b/Modules/Pharmacokinetics/files.cmake index bc0e5e5661..03780a2490 100644 --- a/Modules/Pharmacokinetics/files.cmake +++ b/Modules/Pharmacokinetics/files.cmake @@ -1,64 +1,67 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES Common/mitkAterialInputFunctionGenerator.cpp Common/mitkAIFParametrizerHelper.cpp Common/mitkConcentrationCurveGenerator.cpp Common/mitkDescriptionParameterImageGeneratorBase.cpp Common/mitkPixelBasedDescriptionParameterImageGenerator.cpp DescriptionParameters/mitkCurveDescriptionParameterBase.cpp DescriptionParameters/mitkAreaUnderTheCurveDescriptionParameter.cpp DescriptionParameters/mitkAreaUnderFirstMomentDescriptionParameter.cpp DescriptionParameters/mitkMeanResidenceTimeDescriptionParameter.cpp DescriptionParameters/mitkTimeToPeakCurveDescriptionParameter.cpp DescriptionParameters/mitkMaximumCurveDescriptionParameter.cpp Functors/mitkCurveParameterFunctor.cpp Models/mitkAIFBasedModelBase.cpp Models/mitkDescriptivePharmacokineticBrixModel.cpp Models/mitkDescriptivePharmacokineticBrixModelFactory.cpp Models/mitkDescriptivePharmacokineticBrixModelValueBasedParameterizer.cpp Models/mitkDescriptivePharmacokineticBrixModelParameterizer.cpp Models/mitkThreeStepLinearModel.cpp Models/mitkThreeStepLinearModelFactory.cpp Models/mitkThreeStepLinearModelParameterizer.cpp Models/mitkTwoCompartmentExchangeModel.cpp Models/mitkTwoCompartmentExchangeModelFactory.cpp Models/mitkTwoCompartmentExchangeModelParameterizer.cpp Models/mitkNumericTwoCompartmentExchangeModel.cpp Models/mitkNumericTwoCompartmentExchangeModelFactory.cpp Models/mitkNumericTwoCompartmentExchangeModelParameterizer.cpp Models/mitkExtendedToftsModel.cpp Models/mitkExtendedToftsModelFactory.cpp Models/mitkExtendedToftsModelParameterizer.cpp Models/mitkStandardToftsModel.cpp Models/mitkStandardToftsModelFactory.cpp Models/mitkStandardToftsModelParameterizer.cpp Models/mitkOneTissueCompartmentModel.cpp Models/mitkOneTissueCompartmentModelFactory.cpp Models/mitkOneTissueCompartmentModelParameterizer.cpp Models/mitkExtendedOneTissueCompartmentModel.cpp Models/mitkExtendedOneTissueCompartmentModelFactory.cpp Models/mitkExtendedOneTissueCompartmentModelParameterizer.cpp + Models/mitkTwoStepLinearModel.cpp + Models/mitkTwoStepLinearModelFactory.cpp + Models/mitkTwoStepLinearModelParameterizer.cpp Models/mitkTwoTissueCompartmentModel.cpp Models/mitkTwoTissueCompartmentModelFactory.cpp Models/mitkTwoTissueCompartmentModelParameterizer.cpp Models/mitkTwoTissueCompartmentFDGModel.cpp Models/mitkTwoTissueCompartmentFDGModelFactory.cpp Models/mitkTwoTissueCompartmentFDGModelParameterizer.cpp Models/mitkNumericTwoTissueCompartmentModel.cpp Models/mitkNumericTwoTissueCompartmentModelFactory.cpp Models/mitkNumericTwoTissueCompartmentModelParameterizer.cpp SimulationFramework/mitkImageGenerationHelper.cpp ) set(HXX_FILES mitkDICOMSegmentationConstants.h ) set(MOC_H_FILES ) diff --git a/Modules/Pharmacokinetics/include/mitkTwoStepLinearModel.h b/Modules/Pharmacokinetics/include/mitkTwoStepLinearModel.h new file mode 100644 index 0000000000..62f0d544b8 --- /dev/null +++ b/Modules/Pharmacokinetics/include/mitkTwoStepLinearModel.h @@ -0,0 +1,105 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef MITKTWOSTEPLINEARMODEL_H +#define MITKTWOSTEPLINEARMODEL_H + +#include "mitkModelBase.h" + +#include "MitkPharmacokineticsExports.h" + +namespace mitk +{ + + class MITKPHARMACOKINETICS_EXPORT TwoStepLinearModel : public mitk::ModelBase + { + + public: + typedef TwoStepLinearModel Self; + typedef mitk::ModelBase Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + typedef Superclass::ParameterNameType ParameterNameType; + typedef Superclass::ParametersSizeType ParametersSizeType; + + /** Method for creation through the object factory. */ + itkFactorylessNewMacro(Self); + itkCloneMacro(Self); + + /** Run-time type information (and related methods). */ + itkTypeMacro(TwoStepLinearModel, ModelBase) + + static const std::string MODELL_NAME; + static const std::string NAME_PARAMETER_y1; + static const std::string NAME_PARAMETER_t; + static const std::string NAME_PARAMETER_a1; + static const std::string NAME_PARAMETER_a2; + + static const unsigned int POSITION_PARAMETER_y1; + static const unsigned int POSITION_PARAMETER_t; + static const unsigned int POSITION_PARAMETER_a1; + static const unsigned int POSITION_PARAMETER_a2; + + static const unsigned int NUMBER_OF_PARAMETERS; + + virtual std::string GetModelDisplayName() const override; + + virtual std::string GetModelType() const override; + + virtual FunctionStringType GetFunctionString() const override; + + virtual std::string GetXName() const override; + + virtual ParameterNamesType GetParameterNames() const override; + + virtual ParametersSizeType GetNumberOfParameters() const override; + + virtual ParameterNamesType GetStaticParameterNames() const override; + + virtual ParametersSizeType GetNumberOfStaticParameters() const override; + + virtual ParameterNamesType GetDerivedParameterNames() const override; + + virtual ParametersSizeType GetNumberOfDerivedParameters() const override; + + + protected: + TwoStepLinearModel() {}; + virtual ~TwoStepLinearModel(){}; + + + /** + * Actual implementation of the clone method. This method should be reimplemeted + * in subclasses to clone the extra required parameters. + */ + virtual itk::LightObject::Pointer InternalClone() const; + + virtual ModelResultType ComputeModelfunction(const ParametersType& parameters) const; + virtual DerivedParameterMapType ComputeDerivedParameters(const mitk::ModelBase::ParametersType& + parameters) const; + + virtual void SetStaticParameter(const ParameterNameType& name, + const StaticParameterValuesType& values); + virtual StaticParameterValuesType GetStaticParameterValue(const ParameterNameType& name) const; + + private: + + //No copy constructor allowed + TwoStepLinearModel(const Self& source); + void operator=(const Self&); //purposely not implemented + + }; +} + + +#endif // MITKTWOSTEPLINEARMODEL_H diff --git a/Modules/Pharmacokinetics/include/mitkTwoStepLinearModelFactory.h b/Modules/Pharmacokinetics/include/mitkTwoStepLinearModelFactory.h new file mode 100644 index 0000000000..6068d67ab2 --- /dev/null +++ b/Modules/Pharmacokinetics/include/mitkTwoStepLinearModelFactory.h @@ -0,0 +1,55 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef MITKTWOSTEPLINEARMODELFACTORY_H +#define MITKTWOSTEPLINEARMODELFACTORY_H + +#include + +#include "mitkConcreteModelFactoryBase.h" +#include "mitkTwoStepLinearModel.h" +#include "mitkTwoStepLinearModelParameterizer.h" + +#include "MitkPharmacokineticsExports.h" + +namespace mitk +{ + + class MITKPHARMACOKINETICS_EXPORT TwoStepLinearModelFactory : public ConcreteModelFactoryBase + { + public: + mitkClassMacroItkParent(TwoStepLinearModelFactory, ConcreteModelFactoryBase); + itkFactorylessNewMacro(Self); + + /** This function returns the default parameterization (e.g. initial parametrization for fitting) + defined by the model developer for for the given model.*/ + virtual ParametersType GetDefaultInitialParameterization() const; + + protected: + virtual ModelParameterizerBase::Pointer DoCreateParameterizer(const modelFit::ModelFitInfo* fit) + const; + + TwoStepLinearModelFactory(); + + virtual ~TwoStepLinearModelFactory(); + + private: + + //No copy constructor allowed + TwoStepLinearModelFactory(const Self& source); + void operator=(const Self&); //purposely not implemented + + }; + +} + +#endif // MITKTWOSTEPLINEARMODELFACTORY_H diff --git a/Modules/Pharmacokinetics/include/mitkTwoStepLinearModelParameterizer.h b/Modules/Pharmacokinetics/include/mitkTwoStepLinearModelParameterizer.h new file mode 100644 index 0000000000..6edb67b745 --- /dev/null +++ b/Modules/Pharmacokinetics/include/mitkTwoStepLinearModelParameterizer.h @@ -0,0 +1,63 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef MITKTWOSTEPLINEARMODELPARAMETERIZER_H +#define MITKTWOSTEPLINEARMODELPARAMETERIZER_H + +#include "mitkConcreteModelParameterizerBase.h" +#include "mitkTwoStepLinearModel.h" + +namespace mitk +{ + +class MITKPHARMACOKINETICS_EXPORT TwoStepLinearModelParameterizer : public + mitk::ConcreteModelParameterizerBase +{ +public: + typedef TwoStepLinearModelParameterizer Self; + typedef mitk::ConcreteModelParameterizerBase Superclass; + typedef itk::SmartPointer< Self > Pointer; + typedef itk::SmartPointer< const Self > ConstPointer; + + itkTypeMacro(TwoStepLinearModelParameterizer, ConcreteModelParameterizerBase); + itkFactorylessNewMacro(Self); + + typedef typename Superclass::ModelBaseType ModelBaseType; + typedef typename Superclass::ModelBasePointer ModelBasePointer; + + typedef typename Superclass::ModelType ModelType; + typedef typename Superclass::ModelPointer ModelPointer; + + typedef typename Superclass::StaticParameterValueType StaticParameterValueType; + typedef typename Superclass::StaticParameterValuesType StaticParameterValuesType; + typedef typename Superclass::StaticParameterMapType StaticParameterMapType; + + typedef typename Superclass::IndexType IndexType; + + /** This function returns the default parameterization (e.g. initial parametrization for fitting) + defined by the model developer for for the given model.*/ + virtual ParametersType GetDefaultInitialParameterization() const; + +protected: + TwoStepLinearModelParameterizer(){}; + + virtual ~TwoStepLinearModelParameterizer(){}; + +private: + + //No copy constructor allowed + TwoStepLinearModelParameterizer(const Self& source); + void operator=(const Self&); //purposely not implemented +};} + + +#endif // MITKTWOSTEPLINEARMODELPARAMETERIZER_H diff --git a/Modules/Pharmacokinetics/src/Models/mitkTwoStepLinearModel.cpp b/Modules/Pharmacokinetics/src/Models/mitkTwoStepLinearModel.cpp new file mode 100644 index 0000000000..6846ce111c --- /dev/null +++ b/Modules/Pharmacokinetics/src/Models/mitkTwoStepLinearModel.cpp @@ -0,0 +1,184 @@ +/*============================================================================ + +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 "mitkTwoStepLinearModel.h" + +const std::string mitk::TwoStepLinearModel::MODELL_NAME = "Two Step Linear Model"; + +const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_y1 = "BaseValue"; +const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_a1 = "Slope_1"; +const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_t = "Change_Point"; +const std::string mitk::TwoStepLinearModel::NAME_PARAMETER_a2 = "Slope_2"; + +const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_y1 = 0; +const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_t = 1; +const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_a1 = 2; +const unsigned int mitk::TwoStepLinearModel::POSITION_PARAMETER_a2 = 3; + +const unsigned int mitk::TwoStepLinearModel::NUMBER_OF_PARAMETERS = 4; + +std::string mitk::TwoStepLinearModel::GetModelDisplayName() const +{ + return MODELL_NAME; +}; + +std::string mitk::TwoStepLinearModel::GetModelType() const +{ + return "Generic"; +}; + +mitk::TwoStepLinearModel::FunctionStringType mitk::TwoStepLinearModel::GetFunctionString() const +{ + return "Slope_1*t+Y_intercept_1 if tSetTimeGrid(this->m_TimeGrid); + + return newClone.GetPointer(); +}; diff --git a/Modules/Pharmacokinetics/src/Models/mitkTwoStepLinearModelFactory.cpp b/Modules/Pharmacokinetics/src/Models/mitkTwoStepLinearModelFactory.cpp new file mode 100644 index 0000000000..837ce05078 --- /dev/null +++ b/Modules/Pharmacokinetics/src/Models/mitkTwoStepLinearModelFactory.cpp @@ -0,0 +1,35 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + + +#include "mitkTwoStepLinearModelFactory.h" +#include "mitkConcreteModelParameterizerBase.h" + +mitk::TwoStepLinearModelFactory::TwoStepLinearModelFactory() +{ +}; + +mitk::TwoStepLinearModelFactory::~TwoStepLinearModelFactory() +{ +}; + +mitk::ModelParameterizerBase::ParametersType +mitk::TwoStepLinearModelFactory::GetDefaultInitialParameterization() const +{ + return TwoStepLinearModelParameterizer::New()->GetDefaultInitialParameterization(); +}; + +mitk::ModelParameterizerBase::Pointer mitk::TwoStepLinearModelFactory::DoCreateParameterizer( + const modelFit::ModelFitInfo* /*fit*/) const +{ + return ConcreteModelParameterizerBase::New().GetPointer(); +}; diff --git a/Modules/Pharmacokinetics/src/Models/mitkTwoStepLinearModelParameterizer.cpp b/Modules/Pharmacokinetics/src/Models/mitkTwoStepLinearModelParameterizer.cpp new file mode 100644 index 0000000000..29f6b5287c --- /dev/null +++ b/Modules/Pharmacokinetics/src/Models/mitkTwoStepLinearModelParameterizer.cpp @@ -0,0 +1,28 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkTwoStepLinearModelParameterizer.h" + +mitk::TwoStepLinearModelParameterizer::ParametersType +mitk::TwoStepLinearModelParameterizer::GetDefaultInitialParameterization() const +{ + ParametersType initialParameters; + initialParameters.SetSize(4); + initialParameters[mitk:: TwoStepLinearModel::POSITION_PARAMETER_y1] = 0.0; + initialParameters[mitk:: TwoStepLinearModel::POSITION_PARAMETER_t] = 50; + initialParameters[mitk:: TwoStepLinearModel::POSITION_PARAMETER_a1] = 1.0; + initialParameters[mitk:: TwoStepLinearModel::POSITION_PARAMETER_a2] = -1.0; + + return initialParameters; +}; + + diff --git a/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/PAPhantomGenerator.cpp b/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/PAPhantomGenerator.cpp index e309d04de7..1f81720dbb 100644 --- a/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/PAPhantomGenerator.cpp +++ b/Modules/PhotoacousticsLib/MitkPAPhantomGenerator/PAPhantomGenerator.cpp @@ -1,226 +1,225 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include #include #include using namespace mitk::pa; TissueGeneratorParameters::Pointer CreatePhantom_04_04_18_Parameters() { auto returnParameters = TissueGeneratorParameters::New(); returnParameters->SetAirThicknessInMillimeters(12); returnParameters->SetMinBackgroundAbsorption(0.1); returnParameters->SetMaxBackgroundAbsorption(0.1); returnParameters->SetBackgroundAnisotropy(0.9); returnParameters->SetBackgroundScattering(15); returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateNewDirectionVectorInStraightLine); returnParameters->SetDoPartialVolume(true); returnParameters->SetMinNumberOfVessels(1); returnParameters->SetMaxNumberOfVessels(8); returnParameters->SetMinVesselAbsorption(1); returnParameters->SetMaxVesselAbsorption(10); returnParameters->SetMinVesselAnisotropy(0.9); returnParameters->SetMaxVesselAnisotropy(0.9); returnParameters->SetMinVesselBending(0.1); returnParameters->SetMaxVesselBending(0.3); returnParameters->SetMinVesselRadiusInMillimeters(0.25); returnParameters->SetMaxVesselRadiusInMillimeters(4); returnParameters->SetMinVesselScattering(15); returnParameters->SetMaxVesselScattering(15); returnParameters->SetMinVesselZOrigin(1.6); returnParameters->SetMaxVesselZOrigin(4); returnParameters->SetVesselBifurcationFrequency(5000); returnParameters->SetRandomizePhysicalProperties(false); returnParameters->SetSkinThicknessInMillimeters(0); returnParameters->SetUseRngSeed(false); returnParameters->SetVoxelSpacingInCentimeters(0.03); returnParameters->SetXDim(140); returnParameters->SetYDim(100); returnParameters->SetZDim(180); //returnParameters->SetVoxelSpacingInCentimeters(0.015); //returnParameters->SetXDim(280); //returnParameters->SetYDim(200); //returnParameters->SetZDim(360); returnParameters->SetForceVesselsMoveAlongYDirection(true); //returnParameters->SetVoxelSpacingInCentimeters(0.0075); //returnParameters->SetXDim(560); //returnParameters->SetYDim(400); //returnParameters->SetZDim(720); return returnParameters; } struct InputParameters { std::string saveFolderPath; std::string identifyer; std::string exePath; std::string probePath; bool empty; bool verbose; }; InputParameters parseInput(int argc, char* argv[]) { MITK_INFO << "Parsing arguments..."; mitkCommandLineParser parser; parser.setCategory("MITK-Photoacoustics"); parser.setTitle("Mitk Tissue Batch Generator"); parser.setDescription("Creates in silico tissue in batch processing and automatically calculates fluence values for the central slice of the volume."); parser.setContributor("Computer Assisted Medical Interventions, DKFZ"); parser.setArgumentPrefix("--", "-"); parser.beginGroup("Required parameters"); parser.addArgument( "savePath", "s", mitkCommandLineParser::Directory, "Input save folder (directory)", "input save folder", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument( "mitkMcxyz", "m", mitkCommandLineParser::File, "MitkMcxyz binary (file)", "path to the MitkMcxyz binary", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.endGroup(); parser.beginGroup("Optional parameters"); parser.addArgument( "probe", "p", mitkCommandLineParser::File, "xml probe file (file)", "file to the definition of the used probe (*.xml)", us::Any(), true, false, false, mitkCommandLineParser::Output); parser.addArgument( "verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose, or rather debug output"); parser.addArgument( "identifyer", "i", mitkCommandLineParser::String, "Generator identifyer (string)", "A unique identifyer for the calculation instance"); parser.addArgument( "empty-volume", "e", mitkCommandLineParser::Bool, "omit vessel structures (boolean flag)", "Whether to create an empty volume with no structures inside."); parser.endGroup(); InputParameters input; std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size() == 0) exit(-1); if (parsedArgs.count("empty-volume")) { input.empty = us::any_cast(parsedArgs["empty-volume"]); } else { input.empty = false; } if (parsedArgs.count("verbose")) { input.verbose = us::any_cast(parsedArgs["verbose"]); } else { input.verbose = false; } if (parsedArgs.count("savePath")) { input.saveFolderPath = us::any_cast(parsedArgs["savePath"]); } if (parsedArgs.count("mitkMcxyz")) { input.exePath = us::any_cast(parsedArgs["mitkMcxyz"]); } if (parsedArgs.count("probe")) { input.probePath = us::any_cast(parsedArgs["probe"]); } if (parsedArgs.count("identifyer")) { input.identifyer = us::any_cast(parsedArgs["identifyer"]); } else { - auto uid = mitk::UIDGenerator("", 8); - input.identifyer = uid.GetUID(); + input.identifyer = mitk::UIDGenerator().GetUID(); } MITK_INFO << "Parsing arguments...[Done]"; return input; } int main(int argc, char * argv[]) { auto input = parseInput(argc, argv); auto parameters = CreatePhantom_04_04_18_Parameters(); if (input.empty) { parameters->SetMaxNumberOfVessels(0); parameters->SetMinNumberOfVessels(0); } MITK_INFO(input.verbose) << "Generating tissue.."; auto resultTissue = InSilicoTissueGenerator::GenerateInSilicoData(parameters); MITK_INFO(input.verbose) << "Generating tissue..[Done]"; auto inputfolder = std::string(input.saveFolderPath + "input/"); auto outputfolder = std::string(input.saveFolderPath + "output/"); if (!itksys::SystemTools::FileIsDirectory(inputfolder)) { itksys::SystemTools::MakeDirectory(inputfolder); } if (!itksys::SystemTools::FileIsDirectory(outputfolder)) { itksys::SystemTools::MakeDirectory(outputfolder); } std::string savePath = input.saveFolderPath + "input/Phantom_" + input.identifyer + ".nrrd"; mitk::IOUtil::Save(resultTissue->ConvertToMitkImage(), savePath); std::string outputPath = input.saveFolderPath + "output/Phantom_" + input.identifyer + "/"; resultTissue = nullptr; if (!itksys::SystemTools::FileIsDirectory(outputPath)) { itksys::SystemTools::MakeDirectory(outputPath); } outputPath = outputPath + "Fluence_Phantom_" + input.identifyer; MITK_INFO(input.verbose) << "Simulating fluence.."; int result = -4; std::string cmdString = std::string(input.exePath + " -i " + savePath + " -o " + (outputPath + ".nrrd") + " -yo " + "0" + " -p " + input.probePath + " -n 10000000"); MITK_INFO << "Executing: " << cmdString; result = std::system(cmdString.c_str()); MITK_INFO << result; MITK_INFO(input.verbose) << "Simulating fluence..[Done]"; } diff --git a/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/TissueBatchGenerator.cpp b/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/TissueBatchGenerator.cpp index b51659f93a..7f2237754d 100644 --- a/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/TissueBatchGenerator.cpp +++ b/Modules/PhotoacousticsLib/MitkTissueBatchGenerator/TissueBatchGenerator.cpp @@ -1,390 +1,389 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include #include using namespace mitk::pa; TissueGeneratorParameters::Pointer CreateMultiHB_13_02_18_Parameters() { auto returnParameters = TissueGeneratorParameters::New(); returnParameters->SetAirThicknessInMillimeters(1.8); returnParameters->SetMinBackgroundAbsorption(0.001); returnParameters->SetMaxBackgroundAbsorption(0.2); returnParameters->SetBackgroundAnisotropy(0.9); returnParameters->SetBackgroundScattering(15); returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateNewRandomlyDivergingDirectionVector); returnParameters->SetDoPartialVolume(true); returnParameters->SetMinNumberOfVessels(1); returnParameters->SetMaxNumberOfVessels(7); returnParameters->SetMinVesselAbsorption(1); returnParameters->SetMaxVesselAbsorption(12); returnParameters->SetMinVesselAnisotropy(0.9); returnParameters->SetMaxVesselAnisotropy(0.9); returnParameters->SetMinVesselBending(0); returnParameters->SetMaxVesselBending(0.2); returnParameters->SetMinVesselRadiusInMillimeters(0.5); returnParameters->SetMaxVesselRadiusInMillimeters(6); returnParameters->SetMinVesselScattering(15); returnParameters->SetMaxVesselScattering(15); returnParameters->SetMinVesselZOrigin(1); returnParameters->SetMaxVesselZOrigin(3); returnParameters->SetVesselBifurcationFrequency(5000); returnParameters->SetRandomizePhysicalProperties(false); returnParameters->SetSkinThicknessInMillimeters(0); returnParameters->SetUseRngSeed(false); returnParameters->SetVoxelSpacingInCentimeters(0.06); returnParameters->SetXDim(70); returnParameters->SetYDim(100); returnParameters->SetZDim(100); returnParameters->SetMCflag(4); return returnParameters; } TissueGeneratorParameters::Pointer CreateBaselineHB_13_02_18_Parameters() { auto returnParameters = TissueGeneratorParameters::New(); returnParameters->SetAirThicknessInMillimeters(1.8); returnParameters->SetMinBackgroundAbsorption(0.001); returnParameters->SetMaxBackgroundAbsorption(0.2); returnParameters->SetBackgroundAnisotropy(0.9); returnParameters->SetBackgroundScattering(15); returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateNewRandomlyDivergingDirectionVector); returnParameters->SetDoPartialVolume(true); returnParameters->SetMinNumberOfVessels(1); returnParameters->SetMaxNumberOfVessels(1); returnParameters->SetMinVesselAbsorption(4.73); returnParameters->SetMaxVesselAbsorption(4.73); returnParameters->SetMinVesselAnisotropy(0.9); returnParameters->SetMaxVesselAnisotropy(0.9); returnParameters->SetMinVesselBending(0); returnParameters->SetMaxVesselBending(0.2); returnParameters->SetMinVesselRadiusInMillimeters(3); returnParameters->SetMaxVesselRadiusInMillimeters(3); returnParameters->SetMinVesselScattering(15); returnParameters->SetMaxVesselScattering(15); returnParameters->SetMinVesselZOrigin(1); returnParameters->SetMaxVesselZOrigin(3); returnParameters->SetVesselBifurcationFrequency(5000); returnParameters->SetRandomizePhysicalProperties(false); returnParameters->SetSkinThicknessInMillimeters(0); returnParameters->SetUseRngSeed(false); returnParameters->SetVoxelSpacingInCentimeters(0.06); returnParameters->SetXDim(70); returnParameters->SetYDim(100); returnParameters->SetZDim(100); returnParameters->SetMCflag(4); return returnParameters; } TissueGeneratorParameters::Pointer CreateSingleVesselHeterogeneousBackground_08_02_18_Parameters() { auto returnParameters = TissueGeneratorParameters::New(); returnParameters->SetAirThicknessInMillimeters(1.8); returnParameters->SetMinBackgroundAbsorption(0.001); returnParameters->SetMaxBackgroundAbsorption(0.2); returnParameters->SetBackgroundAnisotropy(0.9); returnParameters->SetBackgroundScattering(15); returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateNewRandomlyDivergingDirectionVector); returnParameters->SetDoPartialVolume(true); returnParameters->SetMinNumberOfVessels(1); returnParameters->SetMaxNumberOfVessels(1); returnParameters->SetMinVesselAbsorption(1); returnParameters->SetMaxVesselAbsorption(12); returnParameters->SetMinVesselAnisotropy(0.9); returnParameters->SetMaxVesselAnisotropy(0.9); returnParameters->SetMinVesselBending(0); returnParameters->SetMaxVesselBending(0.2); returnParameters->SetMinVesselRadiusInMillimeters(0.5); returnParameters->SetMaxVesselRadiusInMillimeters(6); returnParameters->SetMinVesselScattering(15); returnParameters->SetMaxVesselScattering(15); returnParameters->SetMinVesselZOrigin(1); returnParameters->SetMaxVesselZOrigin(3); returnParameters->SetVesselBifurcationFrequency(5000); returnParameters->SetRandomizePhysicalProperties(false); returnParameters->SetSkinThicknessInMillimeters(0); returnParameters->SetUseRngSeed(false); returnParameters->SetVoxelSpacingInCentimeters(0.06); returnParameters->SetXDim(70); returnParameters->SetYDim(100); returnParameters->SetZDim(100); returnParameters->SetMCflag(4); return returnParameters; } TissueGeneratorParameters::Pointer CreateMultivessel_19_12_17_Parameters() { auto returnParameters = TissueGeneratorParameters::New(); returnParameters->SetAirThicknessInMillimeters(12); returnParameters->SetMinBackgroundAbsorption(0.1); returnParameters->SetMaxBackgroundAbsorption(0.1); returnParameters->SetBackgroundAnisotropy(0.9); returnParameters->SetBackgroundScattering(15); returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateNewRandomlyDivergingDirectionVector); returnParameters->SetDoPartialVolume(true); returnParameters->SetMinNumberOfVessels(1); returnParameters->SetMaxNumberOfVessels(7); returnParameters->SetMinVesselAbsorption(2); returnParameters->SetMaxVesselAbsorption(8); returnParameters->SetMinVesselAnisotropy(0.9); returnParameters->SetMaxVesselAnisotropy(0.9); returnParameters->SetMinVesselBending(0.1); returnParameters->SetMaxVesselBending(0.3); returnParameters->SetMinVesselRadiusInMillimeters(0.5); returnParameters->SetMaxVesselRadiusInMillimeters(4); returnParameters->SetMinVesselScattering(15); returnParameters->SetMaxVesselScattering(15); returnParameters->SetMinVesselZOrigin(2.2); returnParameters->SetMaxVesselZOrigin(4); returnParameters->SetVesselBifurcationFrequency(5000); returnParameters->SetRandomizePhysicalProperties(false); returnParameters->SetSkinThicknessInMillimeters(0); returnParameters->SetUseRngSeed(false); returnParameters->SetVoxelSpacingInCentimeters(0.06); returnParameters->SetXDim(70); returnParameters->SetYDim(100); returnParameters->SetZDim(100); return returnParameters; } TissueGeneratorParameters::Pointer CreateMultivessel_19_10_17_Parameters() { auto returnParameters = TissueGeneratorParameters::New(); returnParameters->SetAirThicknessInMillimeters(12); returnParameters->SetMinBackgroundAbsorption(0.1); returnParameters->SetMaxBackgroundAbsorption(0.1); returnParameters->SetBackgroundAnisotropy(0.9); returnParameters->SetBackgroundScattering(15); returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateNewRandomlyDivergingDirectionVector); returnParameters->SetDoPartialVolume(true); returnParameters->SetMinNumberOfVessels(1); returnParameters->SetMaxNumberOfVessels(7); returnParameters->SetMinVesselAbsorption(2); returnParameters->SetMaxVesselAbsorption(8); returnParameters->SetMinVesselAnisotropy(0.9); returnParameters->SetMaxVesselAnisotropy(0.9); returnParameters->SetMinVesselBending(0.1); returnParameters->SetMaxVesselBending(0.3); returnParameters->SetMinVesselRadiusInMillimeters(0.5); returnParameters->SetMaxVesselRadiusInMillimeters(4); returnParameters->SetMinVesselScattering(15); returnParameters->SetMaxVesselScattering(15); returnParameters->SetMinVesselZOrigin(2.2); returnParameters->SetMaxVesselZOrigin(4); returnParameters->SetVesselBifurcationFrequency(5000); returnParameters->SetRandomizePhysicalProperties(false); returnParameters->SetSkinThicknessInMillimeters(0); returnParameters->SetUseRngSeed(false); returnParameters->SetVoxelSpacingInCentimeters(0.03); returnParameters->SetXDim(140); returnParameters->SetYDim(200); returnParameters->SetZDim(180); return returnParameters; } TissueGeneratorParameters::Pointer CreateSinglevessel_19_10_17_Parameters() { auto returnParameters = TissueGeneratorParameters::New(); returnParameters->SetAirThicknessInMillimeters(12); returnParameters->SetMinBackgroundAbsorption(0.1); returnParameters->SetMaxBackgroundAbsorption(0.1); returnParameters->SetBackgroundAnisotropy(0.9); returnParameters->SetBackgroundScattering(15); returnParameters->SetCalculateNewVesselPositionCallback(&VesselMeanderStrategy::CalculateNewRandomlyDivergingDirectionVector); returnParameters->SetDoPartialVolume(true); returnParameters->SetMinNumberOfVessels(1); returnParameters->SetMaxNumberOfVessels(1); returnParameters->SetMinVesselAbsorption(2); returnParameters->SetMaxVesselAbsorption(8); returnParameters->SetMinVesselAnisotropy(0.9); returnParameters->SetMaxVesselAnisotropy(0.9); returnParameters->SetMinVesselBending(0.1); returnParameters->SetMaxVesselBending(0.3); returnParameters->SetMinVesselRadiusInMillimeters(0.5); returnParameters->SetMaxVesselRadiusInMillimeters(4); returnParameters->SetMinVesselScattering(15); returnParameters->SetMaxVesselScattering(15); returnParameters->SetMinVesselZOrigin(2.2); returnParameters->SetMaxVesselZOrigin(4); returnParameters->SetVesselBifurcationFrequency(5000); returnParameters->SetRandomizePhysicalProperties(false); returnParameters->SetSkinThicknessInMillimeters(0); returnParameters->SetUseRngSeed(false); returnParameters->SetVoxelSpacingInCentimeters(0.03); returnParameters->SetXDim(140); returnParameters->SetYDim(200); returnParameters->SetZDim(180); return returnParameters; } struct InputParameters { std::string saveFolderPath; std::string identifyer; std::string exePath; std::string probePath; bool verbose; }; InputParameters parseInput(int argc, char* argv[]) { MITK_INFO << "Paring arguments..."; mitkCommandLineParser parser; // set general information parser.setCategory("MITK-Photoacoustics"); parser.setTitle("Mitk Tissue Batch Generator"); parser.setDescription("Creates in silico tissue in batch processing and automatically calculates fluence values for the central slice of the volume."); parser.setContributor("Computer Assisted Medical Interventions, DKFZ"); // how should arguments be prefixed parser.setArgumentPrefix("--", "-"); // add each argument, unless specified otherwise each argument is optional // see mitkCommandLineParser::addArgument for more information parser.beginGroup("Required parameters"); parser.addArgument( "savePath", "s", mitkCommandLineParser::Directory, "Input save folder (directory)", "input save folder", us::Any(), false, false, false, mitkCommandLineParser::Input); parser.addArgument( "mitkMcxyz", "m", mitkCommandLineParser::File, "MitkMcxyz binary (file)", "path to the MitkMcxyz binary", us::Any(), false, false, false, mitkCommandLineParser::Output); parser.endGroup(); parser.beginGroup("Optional parameters"); parser.addArgument( "probe", "p", mitkCommandLineParser::File, "xml probe file (file)", "file to the definition of the used probe (*.xml)", us::Any(), true, false, false, mitkCommandLineParser::Output); parser.addArgument( "verbose", "v", mitkCommandLineParser::Bool, "Verbose Output", "Whether to produce verbose, or rather debug output"); parser.addArgument( "identifyer", "i", mitkCommandLineParser::String, "Generator identifyer (string)", "A unique identifyer for the calculation instance"); InputParameters input; std::map parsedArgs = parser.parseArguments(argc, argv); if (parsedArgs.size() == 0) exit(-1); if (parsedArgs.count("verbose")) { MITK_INFO << "verbose"; input.verbose = us::any_cast(parsedArgs["verbose"]); } else { input.verbose = false; } if (parsedArgs.count("savePath")) { MITK_INFO << "savePath"; input.saveFolderPath = us::any_cast(parsedArgs["savePath"]); } if (parsedArgs.count("mitkMcxyz")) { MITK_INFO << "mitkMcxyz"; input.exePath = us::any_cast(parsedArgs["mitkMcxyz"]); } if (parsedArgs.count("probe")) { MITK_INFO << "probe"; input.probePath = us::any_cast(parsedArgs["probe"]); } if (parsedArgs.count("identifyer")) { MITK_INFO << "identifyer"; input.identifyer = us::any_cast(parsedArgs["identifyer"]); } else { MITK_INFO << "generating identifyer"; - auto uid = mitk::UIDGenerator("", 8); - input.identifyer = uid.GetUID(); + input.identifyer = mitk::UIDGenerator().GetUID(); } MITK_INFO << "Paring arguments...[Done]"; return input; } int main(int argc, char * argv[]) { auto input = parseInput(argc, argv); unsigned int iterationNumber = 0; while (true) { auto parameters = CreateBaselineHB_13_02_18_Parameters(); MITK_INFO(input.verbose) << "Generating tissue.."; auto resultTissue = InSilicoTissueGenerator::GenerateInSilicoData(parameters); MITK_INFO(input.verbose) << "Generating tissue..[Done]"; auto inputfolder = std::string(input.saveFolderPath + "input/"); auto outputfolder = std::string(input.saveFolderPath + "output/"); if (!itksys::SystemTools::FileIsDirectory(inputfolder)) { itksys::SystemTools::MakeDirectory(inputfolder); } if (!itksys::SystemTools::FileIsDirectory(outputfolder)) { itksys::SystemTools::MakeDirectory(outputfolder); } std::string savePath = input.saveFolderPath + "input/BaselineHB_" + input.identifyer + "_" + std::to_string(iterationNumber) + ".nrrd"; mitk::IOUtil::Save(resultTissue->ConvertToMitkImage(), savePath); std::string outputPath = input.saveFolderPath + "output/BaselineHB_" + input.identifyer + "_" + std::to_string(iterationNumber) + "/"; if (!itksys::SystemTools::FileIsDirectory(outputPath)) { itksys::SystemTools::MakeDirectory(outputPath); } outputPath = outputPath + "Fluence_BaselineHB_" + input.identifyer + "_" + std::to_string(iterationNumber); MITK_INFO(input.verbose) << "Simulating fluence.."; for(double yo = -1.8; yo <= 1.81; yo=yo+0.12) { std::string yo_string = std::to_string(round(yo*100)/100.0); int result = -4; if(!input.probePath.empty()) result = std::system(std::string(input.exePath + " -i " + savePath + " -o " + (outputPath + "_yo" + yo_string + ".nrrd") + " -yo " + yo_string + " -p " + input.probePath + " -n 100000000").c_str()); else result = std::system(std::string(input.exePath + " -i " + savePath + " -o " + (outputPath + "_yo" + yo_string + ".nrrd") + " -yo " + yo_string + " -n 100000000").c_str()); MITK_INFO << "yo: " << yo_string << ": " << result; } MITK_INFO(input.verbose) << "Simulating fluence..[Done]"; iterationNumber++; } } diff --git a/Modules/PlanarFigure/CMakeLists.txt b/Modules/PlanarFigure/CMakeLists.txt index 0933cc7d71..af4c93bb9c 100644 --- a/Modules/PlanarFigure/CMakeLists.txt +++ b/Modules/PlanarFigure/CMakeLists.txt @@ -1,7 +1,10 @@ MITK_CREATE_MODULE( - INCLUDE_DIRS PRIVATE src/Algorithms src/DataManagement src/Interactions src/IO src/Rendering - DEPENDS MitkSceneSerializationBase MitkLegacyGL MitkAnnotation + INCLUDE_DIRS PRIVATE src/Algorithms src/DataManagement src/Interactions src/Rendering + DEPENDS MitkLegacyGL MitkAnnotation ) -add_subdirectory(test) +add_subdirectory(autoload/IO) +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/Modules/PlanarFigure/autoload/IO/CMakeLists.txt b/Modules/PlanarFigure/autoload/IO/CMakeLists.txt new file mode 100644 index 0000000000..bdde2f83fc --- /dev/null +++ b/Modules/PlanarFigure/autoload/IO/CMakeLists.txt @@ -0,0 +1,4 @@ +MITK_CREATE_MODULE( PlanarFigureIO + DEPENDS MitkPlanarFigure MitkSceneSerializationBase + AUTOLOAD_WITH MitkCore +) diff --git a/Modules/PlanarFigure/autoload/IO/files.cmake b/Modules/PlanarFigure/autoload/IO/files.cmake new file mode 100644 index 0000000000..d085b5ba90 --- /dev/null +++ b/Modules/PlanarFigure/autoload/IO/files.cmake @@ -0,0 +1,12 @@ +set(H_FILES + mitkPlanarFigureIO.h + mitkPlanarFigureSerializer.h +) + +set(CPP_FILES + mitkPlanarFigureIOActivator.cpp + mitkPlanarFigureIO.cpp + mitkPlanarFigureSerializer.cpp + mitkPlanarFigureSubclassesSerializer.cpp +) + diff --git a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp new file mode 100644 index 0000000000..246a8c2843 --- /dev/null +++ b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.cpp @@ -0,0 +1,560 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include + +#include "mitkCustomMimeType.h" +#include "mitkIOMimeTypes.h" +#include "mitkExceptionMacro.h" + +#include "mitkPlanarAngle.h" +#include "mitkPlanarArrow.h" +#include "mitkPlanarBezierCurve.h" +#include "mitkPlanarCircle.h" +#include "mitkPlanarCross.h" +#include "mitkPlanarDoubleEllipse.h" +#include "mitkPlanarEllipse.h" +#include "mitkPlanarFourPointAngle.h" +#include "mitkPlanarLine.h" +#include "mitkPlanarPolygon.h" +#include "mitkPlanarRectangle.h" +#include "mitkPlanarSubdivisionPolygon.h" +#include "mitkPlaneGeometry.h" + +#include "mitkBasePropertySerializer.h" + +#include + +#include + +namespace mitk +{ + + PlanarFigureIO::PlanarFigureIO() + : AbstractFileIO(PlanarFigure::GetStaticNameOfClass()) + { + std::string category = "MITK PlanarFigure File"; + CustomMimeType customMimeType; + customMimeType.SetCategory(category); + customMimeType.AddExtension("pf"); + this->AbstractFileIOWriter::SetMimeType(customMimeType); + this->AbstractFileIOWriter::SetDescription(category); + + customMimeType.AddExtension("pf"); + customMimeType.AddExtension("PF"); + this->AbstractFileIOReader::SetMimeType(customMimeType); + this->AbstractFileIOReader::SetDescription(category); + + AbstractFileWriter::SetRanking(10); + AbstractFileReader::SetRanking(10); + this->RegisterService(); + } + + IFileIO::ConfidenceLevel PlanarFigureIO::GetWriterConfidenceLevel() const + { + if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) + return Unsupported; + + const auto *input = static_cast(this->GetInput()); + if (input != nullptr) + return Supported; + else + return Unsupported; + } + + void PlanarFigureIO::Write() + { + this->ValidateOutputLocation(); + + mitk::LocaleSwitch localeSwitch("C"); + + TiXmlDocument document; + auto decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... + document.LinkEndChild(decl); + + auto version = new TiXmlElement("Version"); + version->SetAttribute("Writer", __FILE__); + version->SetAttribute("CVSRevision", "$Revision: 17055 $"); + version->SetAttribute("FileVersion", 1); + document.LinkEndChild(version); + + auto pf = dynamic_cast(this->GetInput()); + if (pf == nullptr) + { + mitkThrow() << "Try to safe a BaseData instance as PlanarFigure. That is not a planar figure. This should not happen and is a violated precondition. Please check the program logic."; + } + + auto pfElement = new TiXmlElement("PlanarFigure"); + pfElement->SetAttribute("type", pf->GetNameOfClass()); + document.LinkEndChild(pfElement); + + // Serialize property list of PlanarFigure + mitk::PropertyList::Pointer propertyList = pf->GetPropertyList(); + mitk::PropertyList::PropertyMap::const_iterator it; + for (it = propertyList->GetMap()->begin(); it != propertyList->GetMap()->end(); ++it) + { + // Create seralizer for this property + const mitk::BaseProperty* prop = it->second; + std::string serializerName = std::string(prop->GetNameOfClass()) + "Serializer"; + std::list allSerializers = + itk::ObjectFactoryBase::CreateAllInstance(serializerName.c_str()); + + if (allSerializers.size() != 1) + { + // No or too many serializer(s) found, skip this property + continue; + } + + auto* serializer = + dynamic_cast(allSerializers.begin()->GetPointer()); + if (serializer == nullptr) + { + // Serializer not valid; skip this property + } + + auto keyElement = new TiXmlElement("property"); + keyElement->SetAttribute("key", it->first); + keyElement->SetAttribute("type", prop->GetNameOfClass()); + + serializer->SetProperty(prop); + TiXmlElement* valueElement = nullptr; + try + { + valueElement = serializer->Serialize(); + } + catch (...) + { + } + + if (valueElement == nullptr) + { + // Serialization failed; skip this property + continue; + } + + // Add value to property element + keyElement->LinkEndChild(valueElement); + + // Append serialized property to property list + pfElement->LinkEndChild(keyElement); + } + + // Serialize control points of PlanarFigure + auto controlPointsElement = new TiXmlElement("ControlPoints"); + pfElement->LinkEndChild(controlPointsElement); + for (unsigned int i = 0; i < pf->GetNumberOfControlPoints(); i++) + { + auto vElement = new TiXmlElement("Vertex"); + vElement->SetAttribute("id", i); + vElement->SetDoubleAttribute("x", pf->GetControlPoint(i)[0]); + vElement->SetDoubleAttribute("y", pf->GetControlPoint(i)[1]); + controlPointsElement->LinkEndChild(vElement); + } + auto geoElement = new TiXmlElement("Geometry"); + const auto* planeGeo = dynamic_cast(pf->GetPlaneGeometry()); + if (planeGeo != nullptr) + { + // Write parameters of IndexToWorldTransform of the PlaneGeometry + typedef mitk::Geometry3D::TransformType TransformType; + const TransformType* affineGeometry = planeGeo->GetIndexToWorldTransform(); + const TransformType::ParametersType& parameters = affineGeometry->GetParameters(); + auto vElement = new TiXmlElement("transformParam"); + for (unsigned int i = 0; i < affineGeometry->GetNumberOfParameters(); ++i) + { + std::stringstream paramName; + paramName << "param" << i; + vElement->SetDoubleAttribute(paramName.str().c_str(), parameters.GetElement(i)); + } + geoElement->LinkEndChild(vElement); + + // Write bounds of the PlaneGeometry + typedef mitk::Geometry3D::BoundsArrayType BoundsArrayType; + const BoundsArrayType& bounds = planeGeo->GetBounds(); + vElement = new TiXmlElement("boundsParam"); + for (unsigned int i = 0; i < 6; ++i) + { + std::stringstream boundName; + boundName << "bound" << i; + vElement->SetDoubleAttribute(boundName.str().c_str(), bounds.GetElement(i)); + } + geoElement->LinkEndChild(vElement); + + // Write spacing and origin of the PlaneGeometry + Vector3D spacing = planeGeo->GetSpacing(); + Point3D origin = planeGeo->GetOrigin(); + geoElement->LinkEndChild(this->CreateXMLVectorElement("Spacing", spacing)); + geoElement->LinkEndChild(this->CreateXMLVectorElement("Origin", origin)); + + pfElement->LinkEndChild(geoElement); + } + + if (this->GetOutputStream() != nullptr) + { + *(this->GetOutputStream()) << document; + } + else + { + if (document.SaveFile(this->GetOutputLocation()) == false) + { + MITK_ERROR << "Could not write planar figures to " << this->GetOutputLocation() << "\nTinyXML reports '" << document.ErrorDesc() + << "'"; + throw std::ios_base::failure("Error during writing of planar figure xml file."); + } + } + } + + TiXmlElement* mitk::PlanarFigureIO::CreateXMLVectorElement(const char* name, itk::FixedArray v) + { + auto vElement = new TiXmlElement(name); + vElement->SetDoubleAttribute("x", v.GetElement(0)); + vElement->SetDoubleAttribute("y", v.GetElement(1)); + vElement->SetDoubleAttribute("z", v.GetElement(2)); + return vElement; + } + + IFileIO::ConfidenceLevel PlanarFigureIO::GetReaderConfidenceLevel() const + { + if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) + return Unsupported; + + return Supported; + //Remark: The original reader code assumed that al pf files can be read. + //So no content checking was done. Thus was not implemented while refactoring + //to services yet. But I think it would make sense. + } + + std::vector PlanarFigureIO::DoRead() + { + mitk::LocaleSwitch localeSwitch("C"); + + std::vector results; + + TiXmlDocument document; + + if (this->GetInputStream() != nullptr) + { + std::string s(std::istreambuf_iterator(*(this->GetInputStream())), {}); + document.Parse(s.c_str()); + //Remark: didn't use *(this->GetInputStream()) >> document; + //because our PlanarFigure files version 1 are illformed (multiple top level elements) + //and therefor tinyxml does not read them completly when streamed directly. + //only the first (version element) is read. + } + else + { + if (!document.LoadFile(this->GetInputLocation())) + { + MITK_ERROR << "Could not open/read/parse " << this->GetInputLocation() << ". TinyXML reports: '" << document.ErrorDesc() + << "'. " + << "The error occurred in row " << document.ErrorRow() << ", column " << document.ErrorCol() << "."; + return {}; + } + } + + int fileVersion = 1; + TiXmlElement* versionObject = document.FirstChildElement("Version"); + if (versionObject != nullptr) + { + if (versionObject->QueryIntAttribute("FileVersion", &fileVersion) != TIXML_SUCCESS) + { + MITK_WARN << this->GetInputLocation() << " does not contain version information! Trying version 1 format." << std::endl; + } + } + else + { + MITK_WARN << this->GetInputLocation() << " does not contain version information! Trying version 1 format." << std::endl; + } + if (fileVersion != + 1) // add file version selection and version specific file parsing here, if newer file versions are created + { + MITK_WARN << "File version > 1 is not supported by this reader."; + return {}; + } + + /* file version 1 reader code */ + for (TiXmlElement* pfElement = document.FirstChildElement("PlanarFigure"); pfElement != nullptr; + pfElement = pfElement->NextSiblingElement("PlanarFigure")) + { + std::string type = pfElement->Attribute("type"); + + mitk::PlanarFigure::Pointer planarFigure = nullptr; + if (type == "PlanarAngle") + { + planarFigure = mitk::PlanarAngle::New(); + } + else if (type == "PlanarCircle") + { + planarFigure = mitk::PlanarCircle::New(); + } + else if (type == "PlanarEllipse") + { + planarFigure = mitk::PlanarEllipse::New(); + } + else if (type == "PlanarCross") + { + planarFigure = mitk::PlanarCross::New(); + } + else if (type == "PlanarFourPointAngle") + { + planarFigure = mitk::PlanarFourPointAngle::New(); + } + else if (type == "PlanarLine") + { + planarFigure = mitk::PlanarLine::New(); + } + else if (type == "PlanarPolygon") + { + planarFigure = mitk::PlanarPolygon::New(); + } + else if (type == "PlanarSubdivisionPolygon") + { + planarFigure = mitk::PlanarSubdivisionPolygon::New(); + } + else if (type == "PlanarRectangle") + { + planarFigure = mitk::PlanarRectangle::New(); + } + else if (type == "PlanarArrow") + { + planarFigure = mitk::PlanarArrow::New(); + } + else if (type == "PlanarDoubleEllipse") + { + planarFigure = mitk::PlanarDoubleEllipse::New(); + } + else if (type == "PlanarBezierCurve") + { + planarFigure = mitk::PlanarBezierCurve::New(); + } + else + { + // unknown type + MITK_WARN << "encountered unknown planar figure type '" << type << "'. Skipping this element."; + continue; + } + + // Read properties of the planar figure + for (TiXmlElement* propertyElement = pfElement->FirstChildElement("property"); propertyElement != nullptr; + propertyElement = propertyElement->NextSiblingElement("property")) + { + const char* keya = propertyElement->Attribute("key"); + const std::string key(keya ? keya : ""); + + const char* typea = propertyElement->Attribute("type"); + const std::string type(typea ? typea : ""); + + // hand propertyElement to specific reader + std::stringstream propertyDeserializerClassName; + propertyDeserializerClassName << type << "Serializer"; + + const std::list readers = + itk::ObjectFactoryBase::CreateAllInstance(propertyDeserializerClassName.str().c_str()); + if (readers.size() < 1) + { + MITK_ERROR << "No property reader found for " << type; + } + if (readers.size() > 1) + { + MITK_WARN << "Multiple property readers found for " << type << ". Using arbitrary first one."; + } + + for (auto iter = readers.cbegin(); iter != readers.cend(); ++iter) + { + if (auto* reader = dynamic_cast(iter->GetPointer())) + { + const BaseProperty::Pointer property = reader->Deserialize(propertyElement->FirstChildElement()); + if (property.IsNotNull()) + { + planarFigure->GetPropertyList()->ReplaceProperty(key, property); + } + else + { + MITK_ERROR << "There were errors while loading property '" << key << "' of type " << type + << ". Your data may be corrupted"; + } + break; + } + } + } + + // If we load a planarFigure, it has definitely been placed correctly. + // If we do not set this property here, we cannot load old planarFigures + // without messing up the interaction (PF-Interactor needs this property. + planarFigure->GetPropertyList()->SetBoolProperty("initiallyplaced", true); + + // Which features (length or circumference etc) a figure has is decided by whether it is closed or not + // the function SetClosed has to be called in case of PlanarPolygons to ensure they hold the correct feature + auto* planarPolygon = dynamic_cast(planarFigure.GetPointer()); + if (planarPolygon != nullptr) + { + bool isClosed = false; + planarFigure->GetPropertyList()->GetBoolProperty("closed", isClosed); + planarPolygon->SetClosed(isClosed); + } + + // Read geometry of containing plane + TiXmlElement* geoElement = pfElement->FirstChildElement("Geometry"); + if (geoElement != nullptr) + { + try + { + // Create plane geometry + mitk::PlaneGeometry::Pointer planeGeo = mitk::PlaneGeometry::New(); + + // Extract and set plane transform parameters + const DoubleList transformList = + this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("transformParam"), "param", 12); + + typedef mitk::BaseGeometry::TransformType TransformType; + TransformType::ParametersType parameters; + parameters.SetSize(12); + + unsigned int i; + DoubleList::const_iterator it; + for (it = transformList.cbegin(), i = 0; it != transformList.cend(); ++it, ++i) + { + parameters.SetElement(i, *it); + } + + typedef mitk::BaseGeometry::TransformType TransformType; + TransformType::Pointer affineGeometry = TransformType::New(); + affineGeometry->SetParameters(parameters); + planeGeo->SetIndexToWorldTransform(affineGeometry); + + // Extract and set plane bounds + const DoubleList boundsList = + this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("boundsParam"), "bound", 6); + + typedef mitk::BaseGeometry::BoundsArrayType BoundsArrayType; + + BoundsArrayType bounds; + for (it = boundsList.cbegin(), i = 0; it != boundsList.cend(); ++it, ++i) + { + bounds[i] = *it; + } + + planeGeo->SetBounds(bounds); + + // Extract and set spacing and origin + const Vector3D spacing = this->GetVectorFromXMLNode(geoElement->FirstChildElement("Spacing")); + planeGeo->SetSpacing(spacing); + + const Point3D origin = this->GetPointFromXMLNode(geoElement->FirstChildElement("Origin")); + planeGeo->SetOrigin(origin); + planarFigure->SetPlaneGeometry(planeGeo); + } + catch (...) + { + } + } + TiXmlElement* cpElement = pfElement->FirstChildElement("ControlPoints"); + bool first = true; + if (cpElement != nullptr) + for (TiXmlElement* vertElement = cpElement->FirstChildElement("Vertex"); vertElement != nullptr; + vertElement = vertElement->NextSiblingElement("Vertex")) + { + int id = 0; + mitk::Point2D::ValueType x = 0.0; + mitk::Point2D::ValueType y = 0.0; + if (vertElement->QueryIntAttribute("id", &id) == TIXML_WRONG_TYPE) + return{}; // TODO: can we do a better error handling? + if (vertElement->QueryDoubleAttribute("x", &x) == TIXML_WRONG_TYPE) + return{}; // TODO: can we do a better error handling? + if (vertElement->QueryDoubleAttribute("y", &y) == TIXML_WRONG_TYPE) + return{}; // TODO: can we do a better error handling? + Point2D p; + p.SetElement(0, x); + p.SetElement(1, y); + if (first == true) // needed to set m_FigurePlaced to true + { + planarFigure->PlaceFigure(p); + first = false; + } + planarFigure->SetControlPoint(id, p, true); + } + + // Calculate feature quantities of this PlanarFigure + planarFigure->EvaluateFeatures(); + + // Make sure that no control point is currently selected + planarFigure->DeselectControlPoint(); + + if (planarFigure.IsNotNull()) + { + results.emplace_back(planarFigure); + } + } + + return results; + } + + mitk::PlanarFigureIO::DoubleList mitk::PlanarFigureIO::GetDoubleAttributeListFromXMLNode( + TiXmlElement* e, const char* attributeNameBase, unsigned int count) + { + DoubleList list; + + if (e == nullptr) + throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? + + for (unsigned int i = 0; i < count; ++i) + { + mitk::ScalarType p(-1.0); + std::stringstream attributeName; + attributeName << attributeNameBase << i; + + if (e->QueryDoubleAttribute(attributeName.str().c_str(), &p) == TIXML_WRONG_TYPE) + throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? + list.push_back(p); + } + + return list; + } + + mitk::Point3D mitk::PlanarFigureIO::GetPointFromXMLNode(TiXmlElement* e) + { + if (e == nullptr) + throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? + mitk::Point3D point; + mitk::ScalarType p(-1.0); + if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) + throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? + point.SetElement(0, p); + if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) + throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? + point.SetElement(1, p); + if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) + throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? + point.SetElement(2, p); + return point; + } + + mitk::Vector3D mitk::PlanarFigureIO::GetVectorFromXMLNode(TiXmlElement* e) + { + if (e == nullptr) + throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? + mitk::Vector3D vector; + mitk::ScalarType p(-1.0); + if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) + throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? + vector.SetElement(0, p); + if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) + throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? + vector.SetElement(1, p); + if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) + throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? + vector.SetElement(2, p); + return vector; + } + + PlanarFigureIO *PlanarFigureIO::IOClone() const { return new PlanarFigureIO(*this); } + +} // namespace diff --git a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h new file mode 100644 index 0000000000..215ca99134 --- /dev/null +++ b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIO.h @@ -0,0 +1,92 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef MITK_PLANAR_FIGURE_IO_H +#define MITK_PLANAR_FIGURE_IO_H + +#include +#include + +class TiXmlElement; + +namespace mitk +{ + /** + * Reads/Writes a PlanarFigure to a file + * @ingroup Process + */ + class PlanarFigureIO : public mitk::AbstractFileIO + { + public: + typedef mitk::PlanarFigure InputType; + + PlanarFigureIO(); + + // -------------- AbstractFileReader ------------- + + using AbstractFileReader::Read; + + ConfidenceLevel GetReaderConfidenceLevel() const override; + + // -------------- AbstractFileWriter ------------- + + void Write() override; + ConfidenceLevel GetWriterConfidenceLevel() const override; + + protected: + /** + * @brief Reads a number of mitk::PlanarFigures from the file system + * @return a vector of mitk::PlanarFigures + * @throws throws an mitk::Exception if an error ocurrs during parsing the nrrd header + */ + std::vector> DoRead() override; + + using DoubleList = std::list; + /** + * \brief parses the element for the attributes name0 to nameN, where "name" and the number of attributes + * to read are passed as argument. Returns a list of double vales. + * \param[in] e the TiXmlElement that will be parsed + * \param[in] attributeNameBase the basic name of the parameters + * \param[in] count the number of parameters + * \return returns a mitk::Point3D with the values x,y,z + */ + DoubleList GetDoubleAttributeListFromXMLNode(TiXmlElement* e, const char* attributeNameBase, unsigned int count); + + /** + * \brief parses the element for the attributes x,y,z and returns a mitk::Vector3D filled with these values + * \param[in] e the TiXmlElement that will be parsed + * \return returns a mitk::Vector3D with the values x,y,z + */ + static mitk::Vector3D GetVectorFromXMLNode(TiXmlElement* e); + + /** + * \brief parses the element for the attributes x,y,z and returns a mitk::Point3D filled with these values + * \param[in] e the TiXmlElement that will be parsed + * \return returns a mitk::Point3D with the values x,y,z + */ + static mitk::Point3D GetPointFromXMLNode(TiXmlElement* e); + + /**Documentation + * \brief creates a TinyXML element that contains x, y, and z values + * + * \param[in] name the name of the XML element + * \param[in] v the vector or point that contains the x, y and z values + * \return returns a TiXmlElement named name and three attributes x, y and z. + */ + static TiXmlElement* CreateXMLVectorElement(const char* name, itk::FixedArray v); + + private: + PlanarFigureIO *IOClone() const override; + }; +} // end of namespace mitk + +#endif // MITK_PLANAR_FIGURE_IO_H diff --git a/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIOActivator.cpp b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIOActivator.cpp new file mode 100644 index 0000000000..4c509d33b9 --- /dev/null +++ b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureIOActivator.cpp @@ -0,0 +1,44 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include +#include +#include +#include + +#include "mitkPlanarFigureIO.h" + +namespace mitk +{ + /** + \brief Registers IO services for PlanarFigure module. + */ + class PlanarFigureIOModuleActivator : public us::ModuleActivator + { + std::vector m_FileIOs; + + public: + void Load(us::ModuleContext * /*context*/) override + { + m_FileIOs.push_back(new PlanarFigureIO()); + } + void Unload(us::ModuleContext *) override + { + for (auto &elem : m_FileIOs) + { + delete elem; + } + } + }; +} + +US_EXPORT_MODULE_ACTIVATOR(mitk::PlanarFigureIOModuleActivator) diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureSerializer.cpp b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureSerializer.cpp similarity index 87% rename from Modules/PlanarFigure/src/IO/mitkPlanarFigureSerializer.cpp rename to Modules/PlanarFigure/autoload/IO/mitkPlanarFigureSerializer.cpp index 3cb1fb983b..d9e932a10e 100644 --- a/Modules/PlanarFigure/src/IO/mitkPlanarFigureSerializer.cpp +++ b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureSerializer.cpp @@ -1,62 +1,60 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkPlanarFigureSerializer.h" #include "mitkPlanarFigure.h" -#include "mitkPlanarFigureWriter.h" + +#include #include MITK_REGISTER_SERIALIZER(PlanarFigureSerializer) mitk::PlanarFigureSerializer::PlanarFigureSerializer() { } mitk::PlanarFigureSerializer::~PlanarFigureSerializer() { } std::string mitk::PlanarFigureSerializer::Serialize() { const auto *pf = dynamic_cast(m_Data.GetPointer()); if (pf == nullptr) { MITK_ERROR << " Object at " << (const void *)this->m_Data << " is not an mitk::PlanarFigure. Cannot serialize as PlanarFigure."; return ""; } std::string filename(this->GetUniqueFilenameInWorkingDirectory()); filename += "_"; filename += m_FilenameHint; filename += ".pf"; std::string fullname(m_WorkingDirectory); fullname += "/"; fullname += itksys::SystemTools::ConvertToOutputPath(filename.c_str()); try { - PlanarFigureWriter::Pointer writer = PlanarFigureWriter::New(); - writer->SetFileName(fullname); - writer->SetInput(const_cast(pf)); - writer->Write(); + mitk::IOUtil::Save(pf, fullname); } catch (std::exception &e) { MITK_ERROR << " Error serializing object at " << (const void *)this->m_Data << " to " << fullname << ": " << e.what(); return ""; } return filename; } diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureSerializer.h b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureSerializer.h similarity index 100% rename from Modules/PlanarFigure/src/IO/mitkPlanarFigureSerializer.h rename to Modules/PlanarFigure/autoload/IO/mitkPlanarFigureSerializer.h diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureSubclassesSerializer.cpp b/Modules/PlanarFigure/autoload/IO/mitkPlanarFigureSubclassesSerializer.cpp similarity index 100% rename from Modules/PlanarFigure/src/IO/mitkPlanarFigureSubclassesSerializer.cpp rename to Modules/PlanarFigure/autoload/IO/mitkPlanarFigureSubclassesSerializer.cpp diff --git a/Modules/PlanarFigure/files.cmake b/Modules/PlanarFigure/files.cmake index dc4d3b3be2..321b93d75a 100644 --- a/Modules/PlanarFigure/files.cmake +++ b/Modules/PlanarFigure/files.cmake @@ -1,37 +1,31 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES Algorithms/mitkExtrudePlanarFigureFilter.cpp Algorithms/mitkImageToPlanarFigureFilter.cpp Algorithms/mitkPlanarFigureObjectFactory.cpp Algorithms/mitkPlanarFigureSource.cpp Algorithms/mitkPlanarFigureToPlanarFigureFilter.cpp DataManagement/mitkPlanarAngle.cpp DataManagement/mitkPlanarBezierCurve.cpp DataManagement/mitkPlanarCircle.cpp DataManagement/mitkPlanarDoubleEllipse.cpp DataManagement/mitkPlanarEllipse.cpp DataManagement/mitkPlanarCross.cpp DataManagement/mitkPlanarFigure.cpp DataManagement/mitkPlanarFourPointAngle.cpp DataManagement/mitkPlanarLine.cpp DataManagement/mitkPlanarArrow.cpp DataManagement/mitkPlanarPolygon.cpp DataManagement/mitkPlanarSubdivisionPolygon.cpp DataManagement/mitkPlanarRectangle.cpp DataManagement/mitkPlanarFigureControlPointStyleProperty.cpp - IO/mitkPlanarFigureIOFactory.cpp - IO/mitkPlanarFigureReader.cpp - IO/mitkPlanarFigureWriter.cpp - IO/mitkPlanarFigureWriterFactory.cpp - IO/mitkPlanarFigureSerializer.cpp - IO/mitkPlanarFigureSubclassesSerializer.cpp Interactions/mitkPlanarFigureInteractor.cpp Rendering/mitkPlanarFigureMapper2D.cpp Rendering/mitkPlanarFigureVtkMapper3D.cpp ) set(RESOURCE_FILES Interactions/PlanarFigureConfig.xml Interactions/PlanarFigureInteraction.xml ) diff --git a/Modules/PlanarFigure/include/mitkPlanarFigureObjectFactory.h b/Modules/PlanarFigure/include/mitkPlanarFigureObjectFactory.h index d524154b3f..5bfb6260bb 100644 --- a/Modules/PlanarFigure/include/mitkPlanarFigureObjectFactory.h +++ b/Modules/PlanarFigure/include/mitkPlanarFigureObjectFactory.h @@ -1,56 +1,48 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef PLANARFIGUREOBJECTFACTORY_H_INCLUDED #define PLANARFIGUREOBJECTFACTORY_H_INCLUDED #include "mitkCoreObjectFactoryBase.h" #include namespace mitk { class MITKPLANARFIGURE_EXPORT PlanarFigureObjectFactory : public CoreObjectFactoryBase { public: mitkClassMacro(PlanarFigureObjectFactory, CoreObjectFactoryBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); ~PlanarFigureObjectFactory() override; Mapper::Pointer CreateMapper(mitk::DataNode *node, MapperSlotId slotId) override; void SetDefaultProperties(mitk::DataNode *node) override; const char *GetFileExtensions() override; mitk::CoreObjectFactoryBase::MultimapType GetFileExtensionsMap() override; const char *GetSaveFileExtensions() override; mitk::CoreObjectFactoryBase::MultimapType GetSaveFileExtensionsMap() override; - DEPRECATED(void RegisterIOFactories()); - protected: PlanarFigureObjectFactory(); void CreateFileExtensionsMap(); - MultimapType m_FileExtensionsMap; - MultimapType m_SaveFileExtensionsMap; - - private: - itk::ObjectFactoryBase::Pointer m_PlanarFigureIOFactory; - itk::ObjectFactoryBase::Pointer m_PlanarFigureWriterFactory; }; } #endif // PLANARFIGUREOBJECTFACTORY_H_INCLUDED diff --git a/Modules/PlanarFigure/include/mitkPlanarFigureReader.h b/Modules/PlanarFigure/include/mitkPlanarFigureReader.h deleted file mode 100644 index c565b99055..0000000000 --- a/Modules/PlanarFigure/include/mitkPlanarFigureReader.h +++ /dev/null @@ -1,148 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef _MITK_PlanarFigureReader__H_ -#define _MITK_PlanarFigureReader__H_ - -#include "mitkFileReader.h" -#include "mitkPlanarFigureSource.h" -#include - -#include - -class TiXmlElement; -namespace mitk -{ - /** - * @brief reads xml representations of mitk::PlanarFigure from a file - * - * Reader for xml files containing one or multiple xml represenations of - * mitk::PlanarFigure. If multiple mitk::PlanarFigure are stored in one file, - * these are assigned to multiple outputs of the filter. - * @ingroup MitkPlanarFigureModule - */ - class MITKPLANARFIGURE_EXPORT PlanarFigureReader : public PlanarFigureSource, public FileReader - { - public: - mitkClassMacro(PlanarFigureReader, FileReader); - - itkFactorylessNewMacro(Self); - - itkCloneMacro(Self); - - /** - * @brief Sets the filename of the file to be read - * @param _arg the filename of the point set xml-file - */ - itkSetStringMacro(FileName); - - /** - * @brief Returns the filename of the point set xml-file. - * @returns the filename of the point set xml-file. - */ - itkGetStringMacro(FileName); - - /** - * @warning multiple load not (yet) supported - */ - itkSetStringMacro(FilePrefix); - - /** - * @warning multiple load not (yet) supported - */ - itkGetStringMacro(FilePrefix); - - /** - * @warning multiple load not (yet) supported - */ - itkSetStringMacro(FilePattern); - - /** - * @warning multiple load not (yet) supported - */ - itkGetStringMacro(FilePattern); - - static bool CanReadFile(const std::string filename, const std::string filePrefix, const std::string filePattern); - - /** - * @returns whether the last read attempt was successful or not. - */ - itkGetConstMacro(Success, bool); - - protected: - typedef std::list DoubleList; - - /** - * Constructor - */ - PlanarFigureReader(); - - /** - * Virtual destructor - */ - ~PlanarFigureReader() override; - - /** - * Actually reads the point sets from the given file - */ - void GenerateData() override; - - /** - * Does nothing in the current implementation - */ - void GenerateOutputInformation() override; - - /** - * Resizes the output-objects according to the given number. - * @param num the new number of output objects. - */ - virtual void ResizeOutputs(const unsigned int &num); - - /** - * Checks if the given file has appropriate - * read access. - * @returns true if the file exists and may be read - * or false otherwise. - */ - virtual int CanReadFile(const char *name); - - /** - * \brief parses the element for the attributes x,y,z and returns a mitk::Vector3D filled with these values - * \param[in] e the TiXmlElement that will be parsed - * \return returns a mitk::Vector3D with the values x,y,z - */ - mitk::Vector3D GetVectorFromXMLNode(TiXmlElement *e); - - /** - * \brief parses the element for the attributes x,y,z and returns a mitk::Point3D filled with these values - * \param[in] e the TiXmlElement that will be parsed - * \return returns a mitk::Point3D with the values x,y,z - */ - mitk::Point3D GetPointFromXMLNode(TiXmlElement *e); - - /** - * \brief parses the element for the attributes name0 to nameN, where "name" and the number of attributes - * to read are passed as argument. Returns a list of double vales. - * \param[in] e the TiXmlElement that will be parsed - * \param[in] attributeNameBase the basic name of the parameters - * \param[in] count the number of parameters - * \return returns a mitk::Point3D with the values x,y,z - */ - DoubleList GetDoubleAttributeListFromXMLNode(TiXmlElement *e, const char *attributeNameBase, unsigned int count); - - std::string m_FileName; - std::string m_FilePrefix; - std::string m_FilePattern; - bool m_Success; - }; -} -#endif diff --git a/Modules/PlanarFigure/include/mitkPlanarFigureWriter.h b/Modules/PlanarFigure/include/mitkPlanarFigureWriter.h deleted file mode 100644 index ff4fc6476e..0000000000 --- a/Modules/PlanarFigure/include/mitkPlanarFigureWriter.h +++ /dev/null @@ -1,198 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#ifndef _MITK_PlanarFigure_WRITER__H_ -#define _MITK_PlanarFigure_WRITER__H_ - -#include -#include -#include -#include - -class TiXmlElement; -namespace mitk -{ - /** - * @brief XML-based writer for mitk::PlanarFigures - * - * XML-based writer for mitk::PlanarFigures. - * @ingroup MitkPlanarFigureModule - */ - class MITKPLANARFIGURE_EXPORT PlanarFigureWriter : public mitk::FileWriterWithInformation - { - public: - mitkClassMacro(PlanarFigureWriter, mitk::FileWriter); - - mitkWriterMacro; - - itkFactorylessNewMacro(Self); - - itkCloneMacro(Self); - - typedef mitk::PlanarFigure InputType; - - typedef InputType::Pointer InputTypePointer; - - /** - * Sets the filename of the file to write. - * @param FileName the name of the file to write. - */ - itkSetStringMacro(FileName); - - /** - * @returns the name of the file to be written to disk. - */ - itkGetStringMacro(FileName); - - /** - * @warning multiple write not (yet) supported - */ - itkSetStringMacro(FilePrefix); - - /** - * @warning multiple write not (yet) supported - */ - itkGetStringMacro(FilePrefix); - - /** - * @warning multiple write not (yet) supported - */ - itkSetStringMacro(FilePattern); - - /** - * @warning multiple write not (yet) supported - */ - itkGetStringMacro(FilePattern); - - using Superclass::SetInput; - - /** - * Sets the 0'th input object for the filter. - * @param input the first input for the filter. - */ - void SetInput(InputType *input); - - /** - * Sets the n'th input object for the filter. If num is - * larger than GetNumberOfInputs() the number of inputs is - * resized appropriately. - * @param input the n'th input for the filter. - */ - void SetInput(const unsigned int &num, InputType *input); - - /** - * @returns the 0'th input object of the filter. - */ - PlanarFigure *GetInput(); - - /** - * @param num the index of the desired output object. - * @returns the n'th input object of the filter. - */ - PlanarFigure *GetInput(const unsigned int &num); - - /** - * @brief Return the possible file extensions for the data type associated with the writer - */ - std::vector GetPossibleFileExtensions() override; - - /** - * @brief Return the extension to be added to the filename. - */ - std::string GetFileExtension() override; - - /** - * @brief Check if the Writer can write the Content of the - */ - bool CanWriteDataType(DataNode *) override; - - /** - * @brief Return the MimeType of the saved File. - */ - std::string GetWritenMIMEType() override; - - /** - * @brief Set the DataTreenode as Input. Important: The Writer always have a SetInput-Function. - */ - virtual void SetInput(DataNode *); - - std::string GetSupportedBaseData() const override; - - /** - * @returns whether the last write attempt was successful or not. - */ - itkGetConstMacro(Success, bool); - - const char *GetDefaultFilename() override { return "PlanarFigure.pf"; } - const char *GetFileDialogPattern() override { return "Planar Figure Files (*.pf)"; } - const char *GetDefaultExtension() override { return ".pf"; } - bool CanWriteBaseDataType(BaseData::Pointer data) override - { - return dynamic_cast(data.GetPointer()); - } - void DoWrite(BaseData::Pointer data) override - { - if (CanWriteBaseDataType(data)) - { - this->SetInput(dynamic_cast(data.GetPointer())); - this->Update(); - } - } - - /** - @brief CAUTION: It's up to the user to call this function to release the - memory buffer after use in case the file writer has written to its memory array. - See mitkFileWriter base class. */ - void ReleaseMemory() override; - - protected: - /** - * Constructor. - */ - PlanarFigureWriter(); - - /** - * Virtual destructor. - */ - ~PlanarFigureWriter() override; - - /** - * Writes the a .pf file in xml format that contains all input planar figures - */ - void GenerateData() override; - - /** - * Resizes the number of inputs of the writer. - * The inputs are initialized by empty PlanarFigures - * @param num the new number of inputs - */ - virtual void ResizeInputs(const unsigned int &num); - - /**Documentation - * \brief creates a TinyXML element that contains x, y, and z values - * - * \param[in] name the name of the XML element - * \param[in] v the vector or point that contains the x, y and z values - * \return returns a TiXmlElement named name and three attributes x, y and z. - */ - TiXmlElement *CreateXMLVectorElement(const char *name, itk::FixedArray v); - - std::string m_FileName; - std::string m_FilePrefix; - std::string m_FilePattern; - std::string m_Extension; - std::string m_MimeType; - bool m_Success; - }; -} - -#endif diff --git a/Modules/PlanarFigure/src/Algorithms/mitkPlanarFigureObjectFactory.cpp b/Modules/PlanarFigure/src/Algorithms/mitkPlanarFigureObjectFactory.cpp index ab07c731b3..6ab9c276c3 100644 --- a/Modules/PlanarFigure/src/Algorithms/mitkPlanarFigureObjectFactory.cpp +++ b/Modules/PlanarFigure/src/Algorithms/mitkPlanarFigureObjectFactory.cpp @@ -1,136 +1,117 @@ /*============================================================================ 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 "mitkPlanarFigureObjectFactory.h" -#include "mitkPlanarFigureWriter.h" #include "mitkCoreObjectFactory.h" #include "mitkPlanarFigure.h" -#include "mitkPlanarFigureIOFactory.h" #include "mitkPlanarFigureMapper2D.h" #include "mitkPlanarFigureVtkMapper3D.h" -#include "mitkPlanarFigureWriterFactory.h" #include "mitkVtkGLMapperWrapper.h" typedef std::multimap MultimapType; mitk::PlanarFigureObjectFactory::PlanarFigureObjectFactory() - : m_PlanarFigureIOFactory(PlanarFigureIOFactory::New().GetPointer()), - m_PlanarFigureWriterFactory(PlanarFigureWriterFactory::New().GetPointer()) { static bool alreadyDone = false; if (!alreadyDone) { - itk::ObjectFactoryBase::RegisterFactory(m_PlanarFigureIOFactory); - itk::ObjectFactoryBase::RegisterFactory(m_PlanarFigureWriterFactory); - - m_FileWriters.push_back(PlanarFigureWriter::New().GetPointer()); - CreateFileExtensionsMap(); alreadyDone = true; } } mitk::PlanarFigureObjectFactory::~PlanarFigureObjectFactory() { - itk::ObjectFactoryBase::UnRegisterFactory(m_PlanarFigureWriterFactory); - itk::ObjectFactoryBase::UnRegisterFactory(m_PlanarFigureIOFactory); } mitk::Mapper::Pointer mitk::PlanarFigureObjectFactory::CreateMapper(mitk::DataNode *node, MapperSlotId id) { mitk::Mapper::Pointer newMapper = nullptr; mitk::BaseData *data = node->GetData(); if (dynamic_cast(data) != nullptr) { if (id == mitk::BaseRenderer::Standard2D) { newMapper = mitk::PlanarFigureMapper2D::New(); newMapper->SetDataNode(node); } else if (id == mitk::BaseRenderer::Standard3D) { newMapper = mitk::PlanarFigureVtkMapper3D::New(); newMapper->SetDataNode(node); } } return newMapper; } void mitk::PlanarFigureObjectFactory::SetDefaultProperties(mitk::DataNode *node) { if (node == nullptr) { return; } mitk::DataNode::Pointer nodePointer = node; mitk::PlanarFigure::Pointer pf = dynamic_cast(node->GetData()); if (pf.IsNotNull()) { mitk::PlanarFigureMapper2D::SetDefaultProperties(node); mitk::PlanarFigureVtkMapper3D::SetDefaultProperties(node); node->AddProperty("color", mitk::ColorProperty::New(1.0, 1.0, 1.0), nullptr, true); node->AddProperty("opacity", mitk::FloatProperty::New(0.8), nullptr, true); } } const char *mitk::PlanarFigureObjectFactory::GetFileExtensions() { return ""; } mitk::CoreObjectFactoryBase::MultimapType mitk::PlanarFigureObjectFactory::GetFileExtensionsMap() { - return m_FileExtensionsMap; + return {}; } const char *mitk::PlanarFigureObjectFactory::GetSaveFileExtensions() { - // return ";;Planar Figures (*.pf)"; // for mitk::PlanarFigure and derived classes std::string fileExtension; - this->CreateFileExtensions(m_SaveFileExtensionsMap, fileExtension); + this->CreateFileExtensions({}, fileExtension); return fileExtension.c_str(); }; mitk::CoreObjectFactoryBase::MultimapType mitk::PlanarFigureObjectFactory::GetSaveFileExtensionsMap() { - return m_SaveFileExtensionsMap; + return {}; } void mitk::PlanarFigureObjectFactory::CreateFileExtensionsMap() -{ - m_FileExtensionsMap.insert(std::pair("*.pf", "Planar Figure Files")); - m_SaveFileExtensionsMap.insert(std::pair("*.pf", "Planar Figure Files")); -} - -void mitk::PlanarFigureObjectFactory::RegisterIOFactories() { } struct RegisterPlanarFigureObjectFactory { RegisterPlanarFigureObjectFactory() : m_Factory(mitk::PlanarFigureObjectFactory::New()) { mitk::CoreObjectFactory::GetInstance()->RegisterExtraFactory(m_Factory); } ~RegisterPlanarFigureObjectFactory() { mitk::CoreObjectFactory::GetInstance()->UnRegisterExtraFactory(m_Factory); } mitk::PlanarFigureObjectFactory::Pointer m_Factory; }; static RegisterPlanarFigureObjectFactory registerPlanarFigureObjectFactory; diff --git a/Modules/PlanarFigure/src/DataManagement/mitkPlanarFigure.cpp b/Modules/PlanarFigure/src/DataManagement/mitkPlanarFigure.cpp index d135f3e58a..eb64366acd 100644 --- a/Modules/PlanarFigure/src/DataManagement/mitkPlanarFigure.cpp +++ b/Modules/PlanarFigure/src/DataManagement/mitkPlanarFigure.cpp @@ -1,781 +1,781 @@ /*============================================================================ 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 "mitkPlanarFigure.h" #include "mitkPlaneGeometry.h" #include #include #include mitk::PlanarFigure::PlanarFigure() : m_SelectedControlPoint(-1), m_PreviewControlPointVisible(false), m_FigurePlaced(false), m_PlaneGeometry(nullptr), m_PolyLineUpToDate(false), m_HelperLinesUpToDate(false), m_FeaturesUpToDate(false), m_FeaturesMTime(0) { m_HelperPolyLinesToBePainted = BoolContainerType::New(); m_DisplaySize.first = 0.0; m_DisplaySize.second = 0; this->SetProperty("closed", mitk::BoolProperty::New(false)); // Currently only single-time-step geometries are supported this->InitializeTimeGeometry(1); } mitk::PlanarFigure::PlanarFigure(const Self &other) : BaseData(other), m_ControlPoints(other.m_ControlPoints), m_NumberOfControlPoints(other.m_NumberOfControlPoints), m_SelectedControlPoint(other.m_SelectedControlPoint), m_PolyLines(other.m_PolyLines), m_HelperPolyLines(other.m_HelperPolyLines), m_PreviewControlPoint(other.m_PreviewControlPoint), m_PreviewControlPointVisible(other.m_PreviewControlPointVisible), m_FigurePlaced(other.m_FigurePlaced), - m_PlaneGeometry(other.m_PlaneGeometry), // do not clone since SetPlaneGeometry() doesn't clone either m_PolyLineUpToDate(other.m_PolyLineUpToDate), m_HelperLinesUpToDate(other.m_HelperLinesUpToDate), m_FeaturesUpToDate(other.m_FeaturesUpToDate), m_Features(other.m_Features), m_FeaturesMTime(other.m_FeaturesMTime), m_DisplaySize(other.m_DisplaySize) { m_HelperPolyLinesToBePainted = BoolContainerType::New(); for (unsigned long i = 0; i < other.m_HelperPolyLinesToBePainted->Size(); ++i) { m_HelperPolyLinesToBePainted->InsertElement(i, other.m_HelperPolyLinesToBePainted->GetElement(i)); } + m_PlaneGeometry = dynamic_cast(GetGeometry(0)); } void mitk::PlanarFigure::SetPlaneGeometry(mitk::PlaneGeometry *geometry) { this->SetGeometry(geometry); m_PlaneGeometry = dynamic_cast(GetGeometry(0)); // geometry; } const mitk::PlaneGeometry *mitk::PlanarFigure::GetPlaneGeometry() const { return m_PlaneGeometry; } bool mitk::PlanarFigure::IsClosed() const { mitk::BoolProperty *closed = dynamic_cast(this->GetProperty("closed").GetPointer()); if (closed != nullptr) { return closed->GetValue(); } return false; } void mitk::PlanarFigure::PlaceFigure(const mitk::Point2D &point) { for (unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i) { m_ControlPoints.push_back(this->ApplyControlPointConstraints(i, point)); } m_FigurePlaced = true; m_SelectedControlPoint = 1; } bool mitk::PlanarFigure::AddControlPoint(const mitk::Point2D &point, int position) { // if we already have the maximum number of control points, do nothing if (m_NumberOfControlPoints < this->GetMaximumNumberOfControlPoints()) { // if position has not been defined or position would be the last control point, just append the new one // we also append a new point if we click onto the line between the first two control-points if the second // control-point is selected // -> special case for PlanarCross if (position == -1 || position > (int)m_NumberOfControlPoints - 1 || (position == 1 && m_SelectedControlPoint == 2)) { if (m_ControlPoints.size() > this->GetMaximumNumberOfControlPoints() - 1) { // get rid of deprecated control points in the list. This is necessary // as ::ResetNumberOfControlPoints() only sets the member, does not resize the list! m_ControlPoints.resize(this->GetNumberOfControlPoints()); } m_ControlPoints.push_back(this->ApplyControlPointConstraints(m_NumberOfControlPoints, point)); m_SelectedControlPoint = m_NumberOfControlPoints; } else { // insert the point at the given position and set it as selected point auto iter = m_ControlPoints.begin() + position; m_ControlPoints.insert(iter, this->ApplyControlPointConstraints(position, point)); for (unsigned int i = 0; i < m_ControlPoints.size(); ++i) { if (point == m_ControlPoints.at(i)) { m_SelectedControlPoint = i; } } } // polylines & helperpolylines need to be repainted m_PolyLineUpToDate = false; m_HelperLinesUpToDate = false; m_FeaturesUpToDate = false; // one control point more ++m_NumberOfControlPoints; return true; } else { return false; } } bool mitk::PlanarFigure::SetControlPoint(unsigned int index, const Point2D &point, bool createIfDoesNotExist) { bool controlPointSetCorrectly = false; if (createIfDoesNotExist) { if (m_NumberOfControlPoints <= index) { m_ControlPoints.push_back(this->ApplyControlPointConstraints(index, point)); m_NumberOfControlPoints++; } else { m_ControlPoints.at(index) = this->ApplyControlPointConstraints(index, point); } controlPointSetCorrectly = true; } else if (index < m_NumberOfControlPoints) { m_ControlPoints.at(index) = this->ApplyControlPointConstraints(index, point); controlPointSetCorrectly = true; } else { return false; } if (controlPointSetCorrectly) { m_PolyLineUpToDate = false; m_HelperLinesUpToDate = false; m_FeaturesUpToDate = false; } return controlPointSetCorrectly; } bool mitk::PlanarFigure::SetCurrentControlPoint(const Point2D &point) { if ((m_SelectedControlPoint < 0) || (m_SelectedControlPoint >= (int)m_NumberOfControlPoints)) { return false; } return this->SetControlPoint(m_SelectedControlPoint, point, false); } unsigned int mitk::PlanarFigure::GetNumberOfControlPoints() const { return m_NumberOfControlPoints; } bool mitk::PlanarFigure::SelectControlPoint(unsigned int index) { if (index < this->GetNumberOfControlPoints()) { m_SelectedControlPoint = index; return true; } else { return false; } } bool mitk::PlanarFigure::DeselectControlPoint() { bool wasSelected = (m_SelectedControlPoint != -1); m_SelectedControlPoint = -1; return wasSelected; } void mitk::PlanarFigure::SetPreviewControlPoint(const Point2D &point) { m_PreviewControlPoint = point; m_PreviewControlPointVisible = true; } void mitk::PlanarFigure::ResetPreviewContolPoint() { m_PreviewControlPointVisible = false; } mitk::Point2D mitk::PlanarFigure::GetPreviewControlPoint() const { return m_PreviewControlPoint; } bool mitk::PlanarFigure::IsPreviewControlPointVisible() const { return m_PreviewControlPointVisible; } mitk::Point2D mitk::PlanarFigure::GetControlPoint(unsigned int index) const { if (index < m_NumberOfControlPoints) { return m_ControlPoints.at(index); } itkExceptionMacro(<< "GetControlPoint(): Invalid index!"); } mitk::Point3D mitk::PlanarFigure::GetWorldControlPoint(unsigned int index) const { Point3D point3D; if ((m_PlaneGeometry != nullptr) && (index < m_NumberOfControlPoints)) { m_PlaneGeometry->Map(m_ControlPoints.at(index), point3D); return point3D; } itkExceptionMacro(<< "GetWorldControlPoint(): Invalid index!"); } const mitk::PlanarFigure::PolyLineType mitk::PlanarFigure::GetPolyLine(unsigned int index) { mitk::PlanarFigure::PolyLineType polyLine; if (index > m_PolyLines.size() || !m_PolyLineUpToDate) { this->GeneratePolyLine(); m_PolyLineUpToDate = true; } return m_PolyLines.at(index); } const mitk::PlanarFigure::PolyLineType mitk::PlanarFigure::GetPolyLine(unsigned int index) const { return m_PolyLines.at(index); } void mitk::PlanarFigure::ClearPolyLines() { for (std::vector::size_type i = 0; i < m_PolyLines.size(); i++) { m_PolyLines.at(i).clear(); } m_PolyLineUpToDate = false; } const mitk::PlanarFigure::PolyLineType mitk::PlanarFigure::GetHelperPolyLine(unsigned int index, double mmPerDisplayUnit, unsigned int displayHeight) { mitk::PlanarFigure::PolyLineType helperPolyLine; if (index < m_HelperPolyLines.size()) { // m_HelperLinesUpToDate does not cover changes in zoom-level, so we have to check previous values of the // two parameters as well if (!m_HelperLinesUpToDate || m_DisplaySize.first != mmPerDisplayUnit || m_DisplaySize.second != displayHeight) { this->GenerateHelperPolyLine(mmPerDisplayUnit, displayHeight); m_HelperLinesUpToDate = true; // store these parameters to be able to check next time if somebody zoomed in or out m_DisplaySize.first = mmPerDisplayUnit; m_DisplaySize.second = displayHeight; } helperPolyLine = m_HelperPolyLines.at(index); } return helperPolyLine; } void mitk::PlanarFigure::ClearHelperPolyLines() { for (std::vector::size_type i = 0; i < m_HelperPolyLines.size(); i++) { m_HelperPolyLines.at(i).clear(); } m_HelperLinesUpToDate = false; } /** \brief Returns the number of features available for this PlanarFigure * (such as, radius, area, ...). */ unsigned int mitk::PlanarFigure::GetNumberOfFeatures() const { return m_Features.size(); } int mitk::PlanarFigure::GetControlPointForPolylinePoint(int indexOfPolylinePoint, int /*polyLineIndex*/) const { return indexOfPolylinePoint; } const char *mitk::PlanarFigure::GetFeatureName(unsigned int index) const { if (index < m_Features.size()) { return m_Features[index].Name.c_str(); } else { return nullptr; } } const char *mitk::PlanarFigure::GetFeatureUnit(unsigned int index) const { if (index < m_Features.size()) { return m_Features[index].Unit.c_str(); } else { return nullptr; } } double mitk::PlanarFigure::GetQuantity(unsigned int index) const { if (index < m_Features.size()) { return m_Features[index].Quantity; } else { return 0.0; } } bool mitk::PlanarFigure::IsFeatureActive(unsigned int index) const { if (index < m_Features.size()) { return m_Features[index].Active; } else { return false; } } bool mitk::PlanarFigure::IsFeatureVisible(unsigned int index) const { if (index < m_Features.size()) { return m_Features[index].Visible; } else { return false; } } void mitk::PlanarFigure::SetFeatureVisible(unsigned int index, bool visible) { if (index < m_Features.size()) { m_Features[index].Visible = visible; } } void mitk::PlanarFigure::EvaluateFeatures() { if (!m_FeaturesUpToDate || !m_PolyLineUpToDate) { if (!m_PolyLineUpToDate) { this->GeneratePolyLine(); } this->EvaluateFeaturesInternal(); m_FeaturesUpToDate = true; } } void mitk::PlanarFigure::UpdateOutputInformation() { // Bounds are NOT calculated here, since the PlaneGeometry defines a fixed // frame (= bounds) for the planar figure. Superclass::UpdateOutputInformation(); this->GetTimeGeometry()->Update(); } void mitk::PlanarFigure::SetRequestedRegionToLargestPossibleRegion() { } bool mitk::PlanarFigure::RequestedRegionIsOutsideOfTheBufferedRegion() { return false; } bool mitk::PlanarFigure::VerifyRequestedRegion() { return true; } void mitk::PlanarFigure::SetRequestedRegion(const itk::DataObject * /*data*/) { } void mitk::PlanarFigure::ResetNumberOfControlPoints(int numberOfControlPoints) { // DO NOT resize the list here, will cause crash!! m_NumberOfControlPoints = numberOfControlPoints; } mitk::Point2D mitk::PlanarFigure::ApplyControlPointConstraints(unsigned int /*index*/, const Point2D &point) { if (m_PlaneGeometry == nullptr) { return point; } Point2D indexPoint; m_PlaneGeometry->WorldToIndex(point, indexPoint); BoundingBox::BoundsArrayType bounds = m_PlaneGeometry->GetBounds(); if (indexPoint[0] < bounds[0]) { indexPoint[0] = bounds[0]; } if (indexPoint[0] > bounds[1]) { indexPoint[0] = bounds[1]; } if (indexPoint[1] < bounds[2]) { indexPoint[1] = bounds[2]; } if (indexPoint[1] > bounds[3]) { indexPoint[1] = bounds[3]; } Point2D constrainedPoint; m_PlaneGeometry->IndexToWorld(indexPoint, constrainedPoint); return constrainedPoint; } unsigned int mitk::PlanarFigure::AddFeature(const char *featureName, const char *unitName) { unsigned int index = m_Features.size(); Feature newFeature(featureName, unitName); m_Features.push_back(newFeature); return index; } void mitk::PlanarFigure::SetFeatureName(unsigned int index, const char *featureName) { if (index < m_Features.size()) { m_Features[index].Name = featureName; } } void mitk::PlanarFigure::SetFeatureUnit(unsigned int index, const char *unitName) { if (index < m_Features.size()) { m_Features[index].Unit = unitName; } } void mitk::PlanarFigure::SetQuantity(unsigned int index, double quantity) { if (index < m_Features.size()) { m_Features[index].Quantity = quantity; } } void mitk::PlanarFigure::ActivateFeature(unsigned int index) { if (index < m_Features.size()) { m_Features[index].Active = true; } } void mitk::PlanarFigure::DeactivateFeature(unsigned int index) { if (index < m_Features.size()) { m_Features[index].Active = false; } } void mitk::PlanarFigure::InitializeTimeGeometry(unsigned int timeSteps) { mitk::PlaneGeometry::Pointer geometry2D = mitk::PlaneGeometry::New(); geometry2D->Initialize(); // The geometry is propagated automatically to all time steps, // if EvenlyTimed is true... ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(geometry2D, timeSteps); SetTimeGeometry(timeGeometry); } void mitk::PlanarFigure::PrintSelf(std::ostream &os, itk::Indent indent) const { Superclass::PrintSelf(os, indent); os << indent << this->GetNameOfClass() << ":\n"; if (this->IsClosed()) os << indent << "This figure is closed\n"; else os << indent << "This figure is not closed\n"; os << indent << "Minimum number of control points: " << this->GetMinimumNumberOfControlPoints() << std::endl; os << indent << "Maximum number of control points: " << this->GetMaximumNumberOfControlPoints() << std::endl; os << indent << "Current number of control points: " << this->GetNumberOfControlPoints() << std::endl; os << indent << "Control points:" << std::endl; for (unsigned int i = 0; i < this->GetNumberOfControlPoints(); ++i) { // os << indent.GetNextIndent() << i << ": " << m_ControlPoints->ElementAt( i ) << std::endl; os << indent.GetNextIndent() << i << ": " << m_ControlPoints.at(i) << std::endl; } os << indent << "Geometry:\n"; this->GetPlaneGeometry()->Print(os, indent.GetNextIndent()); } unsigned short mitk::PlanarFigure::GetPolyLinesSize() { if (!m_PolyLineUpToDate) { this->GeneratePolyLine(); m_PolyLineUpToDate = true; } return m_PolyLines.size(); } unsigned short mitk::PlanarFigure::GetHelperPolyLinesSize() const { return m_HelperPolyLines.size(); } bool mitk::PlanarFigure::IsHelperToBePainted(unsigned int index) const { return m_HelperPolyLinesToBePainted->GetElement(index); } bool mitk::PlanarFigure::ResetOnPointSelect() { return false; } bool mitk::PlanarFigure::ResetOnPointSelectNeeded() const { return false; } void mitk::PlanarFigure::RemoveControlPoint(unsigned int index) { if (index > m_ControlPoints.size()) return; if ((m_ControlPoints.size() - 1) < this->GetMinimumNumberOfControlPoints()) return; ControlPointListType::iterator iter; iter = m_ControlPoints.begin() + index; m_ControlPoints.erase(iter); m_PolyLineUpToDate = false; m_HelperLinesUpToDate = false; m_FeaturesUpToDate = false; --m_NumberOfControlPoints; } void mitk::PlanarFigure::RemoveLastControlPoint() { RemoveControlPoint(m_ControlPoints.size() - 1); } void mitk::PlanarFigure::SetNumberOfPolyLines(unsigned int numberOfPolyLines) { m_PolyLines.resize(numberOfPolyLines); } void mitk::PlanarFigure::SetNumberOfHelperPolyLines(unsigned int numberOfHerlperPolyLines) { m_HelperPolyLines.resize(numberOfHerlperPolyLines); } void mitk::PlanarFigure::AppendPointToPolyLine(unsigned int index, PolyLineElement element) { if (index < m_PolyLines.size()) { m_PolyLines[index].push_back(element); m_PolyLineUpToDate = false; } else { MITK_ERROR << "Tried to add point to PolyLine " << index + 1 << ", although only " << m_PolyLines.size() << " exists"; } } void mitk::PlanarFigure::AppendPointToHelperPolyLine(unsigned int index, PolyLineElement element) { if (index < m_HelperPolyLines.size()) { m_HelperPolyLines[index].push_back(element); m_HelperLinesUpToDate = false; } else { MITK_ERROR << "Tried to add point to HelperPolyLine " << index + 1 << ", although only " << m_HelperPolyLines.size() << " exists"; } } bool mitk::PlanarFigure::Equals(const mitk::PlanarFigure &other) const { // check geometries if (this->GetPlaneGeometry() && other.GetPlaneGeometry()) { if (!Equal(*(this->GetPlaneGeometry()), *(other.GetPlaneGeometry()), mitk::eps, true)) { return false; } } else { MITK_ERROR << "Geometry is not equal"; return false; } // check isPlaced member if (this->m_FigurePlaced != other.m_FigurePlaced) { MITK_ERROR << "Is_Placed is not equal"; return false; } // check closed property if (this->IsClosed() != other.IsClosed()) { MITK_ERROR << "Is_closed is not equal"; return false; } // check poly lines if (this->m_PolyLines.size() != other.m_PolyLines.size()) { return false; } else { auto itThis = this->m_PolyLines.begin(); auto itEnd = this->m_PolyLines.end(); auto itOther = other.m_PolyLines.begin(); while (itThis != itEnd) { if (itThis->size() != itOther->size()) return false; else { auto itLineThis = itThis->begin(); auto itLineEnd = itThis->end(); auto itLineOther = itOther->begin(); while (itLineThis != itLineEnd) { Point2D p1 = *itLineThis; Point2D p2 = *itLineOther; ScalarType delta = fabs(p1[0] - p2[0]) + fabs(p1[1] - p2[1]); if (delta > .001) { MITK_ERROR << "Poly line is not equal"; MITK_ERROR << p1 << "/" << p2; return false; } ++itLineThis; ++itLineOther; } } ++itThis; ++itOther; } } // check features if (this->GetNumberOfFeatures() != other.GetNumberOfFeatures()) { MITK_ERROR << "Number of Features is Different"; return false; } else { auto itThis = m_Features.begin(); auto itEnd = m_Features.end(); auto itOther = other.m_Features.begin(); while (itThis != itEnd) { if ((itThis->Quantity - itOther->Quantity) > .001) { MITK_ERROR << "Quantity is Different" << itThis->Quantity << "/" << itOther->Quantity; return false; } if (itThis->Unit.compare(itOther->Unit) != 0) { MITK_ERROR << "Unit is Different" << itThis->Unit << "/" << itOther->Unit; return false; } if (itThis->Name.compare(itOther->Name) != 0) { MITK_ERROR << "Name of Measure is Different " << itThis->Name << "/ " << itOther->Name; ; return false; } ++itThis; ++itOther; } } return true; } bool mitk::Equal(const mitk::PlanarFigure &leftHandSide, const mitk::PlanarFigure &rightHandSide, ScalarType /*eps*/, bool /*verbose*/) { // FIXME: use eps and verbose return leftHandSide.Equals(rightHandSide); } diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureIOFactory.cpp b/Modules/PlanarFigure/src/IO/mitkPlanarFigureIOFactory.cpp deleted file mode 100644 index 9c6d67781c..0000000000 --- a/Modules/PlanarFigure/src/IO/mitkPlanarFigureIOFactory.cpp +++ /dev/null @@ -1,37 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkPlanarFigureIOFactory.h" -#include "mitkIOAdapter.h" -#include "mitkPlanarFigureReader.h" - -#include "itkVersion.h" - -namespace mitk -{ - PlanarFigureIOFactory::PlanarFigureIOFactory() - { - this->RegisterOverride("mitkIOAdapter", - "mitkPlanarFigureReader", - "mitk PlanarFigure IO", - true, - itk::CreateObjectFunction>::New()); - } - - PlanarFigureIOFactory::~PlanarFigureIOFactory() {} - const char *PlanarFigureIOFactory::GetITKSourceVersion() const { return ITK_SOURCE_VERSION; } - const char *PlanarFigureIOFactory::GetDescription() const - { - return "PlanarFigure IO Factory, allows the loading of .pf files"; - } - -} // end namespace mitk diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureIOFactory.h b/Modules/PlanarFigure/src/IO/mitkPlanarFigureIOFactory.h deleted file mode 100644 index f7f3b161ef..0000000000 --- a/Modules/PlanarFigure/src/IO/mitkPlanarFigureIOFactory.h +++ /dev/null @@ -1,68 +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 __mitkPlanarFigureIOFactory_h -#define __mitkPlanarFigureIOFactory_h - -#ifdef _MSC_VER -#pragma warning(disable : 4786) -#endif - -#include "itkObjectFactoryBase.h" -#include "mitkBaseData.h" - -namespace mitk -{ - //##Documentation - //## @brief Create instances of PlanarFigureReader objects using an object factory. - //## - //## @ingroup MitkPlanarFigureModule - class PlanarFigureIOFactory : public itk::ObjectFactoryBase - { - public: - /** Standard class typedefs. */ - typedef PlanarFigureIOFactory Self; - typedef itk::ObjectFactoryBase Superclass; - typedef itk::SmartPointer Pointer; - typedef itk::SmartPointer ConstPointer; - - /** Class methods used to interface with the registered factories. */ - const char *GetITKSourceVersion(void) const override; - const char *GetDescription(void) const override; - - /** Method for class instantiation. */ - itkFactorylessNewMacro(Self); - static PlanarFigureIOFactory *FactoryNew() { return new PlanarFigureIOFactory; } - /** Run-time type information (and related methods). */ - itkTypeMacro(PlanarFigureIOFactory, ObjectFactoryBase); - - /** - * Register one factory of this type - * \deprecatedSince{2013_09} - */ - DEPRECATED(static void RegisterOneFactory(void)) - { - PlanarFigureIOFactory::Pointer PlanarFigureIOFactory = PlanarFigureIOFactory::New(); - ObjectFactoryBase::RegisterFactory(PlanarFigureIOFactory); - } - - protected: - PlanarFigureIOFactory(); - ~PlanarFigureIOFactory() override; - - private: - PlanarFigureIOFactory(const Self &); // purposely not implemented - void operator=(const Self &); // purposely not implemented - }; - -} // end namespace mitk - -#endif diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp b/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp deleted file mode 100644 index 5aa97568cc..0000000000 --- a/Modules/PlanarFigure/src/IO/mitkPlanarFigureReader.cpp +++ /dev/null @@ -1,438 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkPlanarFigureReader.h" - -#include "mitkPlanarAngle.h" -#include "mitkPlanarArrow.h" -#include "mitkPlanarBezierCurve.h" -#include "mitkPlanarCircle.h" -#include "mitkPlanarCross.h" -#include "mitkPlanarDoubleEllipse.h" -#include "mitkPlanarEllipse.h" -#include "mitkPlanarFourPointAngle.h" -#include "mitkPlanarLine.h" -#include "mitkPlanarPolygon.h" -#include "mitkPlanarRectangle.h" -#include "mitkPlanarSubdivisionPolygon.h" -#include "mitkPlaneGeometry.h" - -#include "mitkBasePropertySerializer.h" -#include - -#include -#include - -mitk::PlanarFigureReader::PlanarFigureReader() - : PlanarFigureSource(), FileReader(), m_FileName(""), m_FilePrefix(""), m_FilePattern(""), m_Success(false) -{ - this->SetNumberOfRequiredOutputs(1); - this->SetNumberOfIndexedOutputs(1); - this->SetNthOutput(0, this->MakeOutput(0)); - - m_CanReadFromMemory = true; - - // this->Modified(); - // this->GetOutput()->Modified(); - // this->GetOutput()->ReleaseData(); -} - -mitk::PlanarFigureReader::~PlanarFigureReader() -{ -} - -void mitk::PlanarFigureReader::GenerateData() -{ - mitk::LocaleSwitch localeSwitch("C"); - m_Success = false; - this->SetNumberOfIndexedOutputs(0); // reset all outputs, we add new ones depending on the file content - - TiXmlDocument document; - - if (m_ReadFromMemory) - { - if (m_MemoryBuffer == nullptr || m_MemorySize == 0) - { - // check - itkWarningMacro(<< "Sorry, memory buffer has not been set!"); - return; - } - if (m_MemoryBuffer[m_MemorySize - 1] == '\0') - { - document.Parse(m_MemoryBuffer); - } - else - { - auto tmpArray = new char[(int)m_MemorySize + 1]; - tmpArray[m_MemorySize] = '\0'; - memcpy(tmpArray, m_MemoryBuffer, m_MemorySize); - - document.Parse(m_MemoryBuffer); - - delete[] tmpArray; - } - } - else - { - if (m_FileName.empty()) - { - itkWarningMacro(<< "Sorry, filename has not been set!"); - return; - } - if (this->CanReadFile(m_FileName.c_str()) == false) - { - itkWarningMacro(<< "Sorry, can't read file " << m_FileName << "!"); - return; - } - if (!document.LoadFile(m_FileName)) - { - MITK_ERROR << "Could not open/read/parse " << m_FileName << ". TinyXML reports: '" << document.ErrorDesc() - << "'. " - << "The error occurred in row " << document.ErrorRow() << ", column " << document.ErrorCol() << "."; - return; - } - } - - int fileVersion = 1; - TiXmlElement *versionObject = document.FirstChildElement("Version"); - if (versionObject != nullptr) - { - if (versionObject->QueryIntAttribute("FileVersion", &fileVersion) != TIXML_SUCCESS) - { - MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl; - } - } - else - { - MITK_WARN << m_FileName << " does not contain version information! Trying version 1 format." << std::endl; - } - if (fileVersion != - 1) // add file version selection and version specific file parsing here, if newer file versions are created - { - MITK_WARN << "File version > 1 is not supported by this reader."; - return; - } - - /* file version 1 reader code */ - for (TiXmlElement *pfElement = document.FirstChildElement("PlanarFigure"); pfElement != nullptr; - pfElement = pfElement->NextSiblingElement("PlanarFigure")) - { - std::string type = pfElement->Attribute("type"); - - mitk::PlanarFigure::Pointer planarFigure = nullptr; - if (type == "PlanarAngle") - { - planarFigure = mitk::PlanarAngle::New(); - } - else if (type == "PlanarCircle") - { - planarFigure = mitk::PlanarCircle::New(); - } - else if (type == "PlanarEllipse") - { - planarFigure = mitk::PlanarEllipse::New(); - } - else if (type == "PlanarCross") - { - planarFigure = mitk::PlanarCross::New(); - } - else if (type == "PlanarFourPointAngle") - { - planarFigure = mitk::PlanarFourPointAngle::New(); - } - else if (type == "PlanarLine") - { - planarFigure = mitk::PlanarLine::New(); - } - else if (type == "PlanarPolygon") - { - planarFigure = mitk::PlanarPolygon::New(); - } - else if (type == "PlanarSubdivisionPolygon") - { - planarFigure = mitk::PlanarSubdivisionPolygon::New(); - } - else if (type == "PlanarRectangle") - { - planarFigure = mitk::PlanarRectangle::New(); - } - else if (type == "PlanarArrow") - { - planarFigure = mitk::PlanarArrow::New(); - } - else if (type == "PlanarDoubleEllipse") - { - planarFigure = mitk::PlanarDoubleEllipse::New(); - } - else if (type == "PlanarBezierCurve") - { - planarFigure = mitk::PlanarBezierCurve::New(); - } - else - { - // unknown type - MITK_WARN << "encountered unknown planar figure type '" << type << "'. Skipping this element."; - continue; - } - - // Read properties of the planar figure - for (TiXmlElement *propertyElement = pfElement->FirstChildElement("property"); propertyElement != nullptr; - propertyElement = propertyElement->NextSiblingElement("property")) - { - const char *keya = propertyElement->Attribute("key"); - const std::string key(keya ? keya : ""); - - const char *typea = propertyElement->Attribute("type"); - const std::string type(typea ? typea : ""); - - // hand propertyElement to specific reader - std::stringstream propertyDeserializerClassName; - propertyDeserializerClassName << type << "Serializer"; - - const std::list readers = - itk::ObjectFactoryBase::CreateAllInstance(propertyDeserializerClassName.str().c_str()); - if (readers.size() < 1) - { - MITK_ERROR << "No property reader found for " << type; - } - if (readers.size() > 1) - { - MITK_WARN << "Multiple property readers found for " << type << ". Using arbitrary first one."; - } - - for (auto iter = readers.cbegin(); iter != readers.cend(); ++iter) - { - if (auto *reader = dynamic_cast(iter->GetPointer())) - { - const BaseProperty::Pointer property = reader->Deserialize(propertyElement->FirstChildElement()); - if (property.IsNotNull()) - { - planarFigure->GetPropertyList()->ReplaceProperty(key, property); - } - else - { - MITK_ERROR << "There were errors while loading property '" << key << "' of type " << type - << ". Your data may be corrupted"; - } - break; - } - } - } - - // If we load a planarFigure, it has definitely been placed correctly. - // If we do not set this property here, we cannot load old planarFigures - // without messing up the interaction (PF-Interactor needs this property. - planarFigure->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - - // Which features (length or circumference etc) a figure has is decided by whether it is closed or not - // the function SetClosed has to be called in case of PlanarPolygons to ensure they hold the correct feature - auto *planarPolygon = dynamic_cast(planarFigure.GetPointer()); - if (planarPolygon != nullptr) - { - bool isClosed = false; - planarFigure->GetPropertyList()->GetBoolProperty("closed", isClosed); - planarPolygon->SetClosed(isClosed); - } - - // Read geometry of containing plane - TiXmlElement *geoElement = pfElement->FirstChildElement("Geometry"); - if (geoElement != nullptr) - { - try - { - // Create plane geometry - mitk::PlaneGeometry::Pointer planeGeo = mitk::PlaneGeometry::New(); - - // Extract and set plane transform parameters - const DoubleList transformList = - this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("transformParam"), "param", 12); - - typedef mitk::BaseGeometry::TransformType TransformType; - TransformType::ParametersType parameters; - parameters.SetSize(12); - - unsigned int i; - DoubleList::const_iterator it; - for (it = transformList.cbegin(), i = 0; it != transformList.cend(); ++it, ++i) - { - parameters.SetElement(i, *it); - } - - typedef mitk::BaseGeometry::TransformType TransformType; - TransformType::Pointer affineGeometry = TransformType::New(); - affineGeometry->SetParameters(parameters); - planeGeo->SetIndexToWorldTransform(affineGeometry); - - // Extract and set plane bounds - const DoubleList boundsList = - this->GetDoubleAttributeListFromXMLNode(geoElement->FirstChildElement("boundsParam"), "bound", 6); - - typedef mitk::BaseGeometry::BoundsArrayType BoundsArrayType; - - BoundsArrayType bounds; - for (it = boundsList.cbegin(), i = 0; it != boundsList.cend(); ++it, ++i) - { - bounds[i] = *it; - } - - planeGeo->SetBounds(bounds); - - // Extract and set spacing and origin - const Vector3D spacing = this->GetVectorFromXMLNode(geoElement->FirstChildElement("Spacing")); - planeGeo->SetSpacing(spacing); - - const Point3D origin = this->GetPointFromXMLNode(geoElement->FirstChildElement("Origin")); - planeGeo->SetOrigin(origin); - planarFigure->SetPlaneGeometry(planeGeo); - } - catch (...) - { - } - } - TiXmlElement *cpElement = pfElement->FirstChildElement("ControlPoints"); - bool first = true; - if (cpElement != nullptr) - for (TiXmlElement *vertElement = cpElement->FirstChildElement("Vertex"); vertElement != nullptr; - vertElement = vertElement->NextSiblingElement("Vertex")) - { - int id = 0; - mitk::Point2D::ValueType x = 0.0; - mitk::Point2D::ValueType y = 0.0; - if (vertElement->QueryIntAttribute("id", &id) == TIXML_WRONG_TYPE) - return; // TODO: can we do a better error handling? - if (vertElement->QueryDoubleAttribute("x", &x) == TIXML_WRONG_TYPE) - return; // TODO: can we do a better error handling? - if (vertElement->QueryDoubleAttribute("y", &y) == TIXML_WRONG_TYPE) - return; // TODO: can we do a better error handling? - Point2D p; - p.SetElement(0, x); - p.SetElement(1, y); - if (first == true) // needed to set m_FigurePlaced to true - { - planarFigure->PlaceFigure(p); - first = false; - } - planarFigure->SetControlPoint(id, p, true); - } - - // Calculate feature quantities of this PlanarFigure - planarFigure->EvaluateFeatures(); - - // Make sure that no control point is currently selected - planarFigure->DeselectControlPoint(); - - // \TODO: what about m_FigurePlaced and m_SelectedControlPoint ?? - this->SetNthOutput(this->GetNumberOfOutputs(), planarFigure); // add planarFigure as new output of this filter - } - - m_Success = true; -} - -mitk::Point3D mitk::PlanarFigureReader::GetPointFromXMLNode(TiXmlElement *e) -{ - if (e == nullptr) - throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? - mitk::Point3D point; - mitk::ScalarType p(-1.0); - if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) - throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? - point.SetElement(0, p); - if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) - throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? - point.SetElement(1, p); - if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) - throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? - point.SetElement(2, p); - return point; -} - -mitk::Vector3D mitk::PlanarFigureReader::GetVectorFromXMLNode(TiXmlElement *e) -{ - if (e == nullptr) - throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? - mitk::Vector3D vector; - mitk::ScalarType p(-1.0); - if (e->QueryDoubleAttribute("x", &p) == TIXML_WRONG_TYPE) - throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? - vector.SetElement(0, p); - if (e->QueryDoubleAttribute("y", &p) == TIXML_WRONG_TYPE) - throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? - vector.SetElement(1, p); - if (e->QueryDoubleAttribute("z", &p) == TIXML_WRONG_TYPE) - throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? - vector.SetElement(2, p); - return vector; -} - -mitk::PlanarFigureReader::DoubleList mitk::PlanarFigureReader::GetDoubleAttributeListFromXMLNode( - TiXmlElement *e, const char *attributeNameBase, unsigned int count) -{ - DoubleList list; - - if (e == nullptr) - throw std::invalid_argument("node invalid"); // TODO: can we do a better error handling? - - for (unsigned int i = 0; i < count; ++i) - { - mitk::ScalarType p(-1.0); - std::stringstream attributeName; - attributeName << attributeNameBase << i; - - if (e->QueryDoubleAttribute(attributeName.str().c_str(), &p) == TIXML_WRONG_TYPE) - throw std::invalid_argument("node malformatted"); // TODO: can we do a better error handling? - list.push_back(p); - } - - return list; -} - -void mitk::PlanarFigureReader::GenerateOutputInformation() -{ -} - -int mitk::PlanarFigureReader::CanReadFile(const char *name) -{ - if (std::string(name).empty()) - return false; - - return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(name)) == - ".pf"); // assume, we can read all .pf files - - // TiXmlDocument document(name); - // if (document.LoadFile() == false) - // return false; - // return (document.FirstChildElement("PlanarFigure") != nullptr); -} - -bool mitk::PlanarFigureReader::CanReadFile(const std::string filename, const std::string, const std::string) -{ - if (filename.empty()) - return false; - - return (itksys::SystemTools::LowerCase(itksys::SystemTools::GetFilenameLastExtension(filename)) == - ".pf"); // assume, we can read all .pf files - - // TiXmlDocument document(filename); - // if (document.LoadFile() == false) - // return false; - // return (document.FirstChildElement("PlanarFigure") != nullptr); -} - -void mitk::PlanarFigureReader::ResizeOutputs(const unsigned int &num) -{ - unsigned int prevNum = this->GetNumberOfOutputs(); - this->SetNumberOfIndexedOutputs(num); - for (unsigned int i = prevNum; i < num; ++i) - { - this->SetNthOutput(i, this->MakeOutput(i).GetPointer()); - } -} diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureWriter.cpp b/Modules/PlanarFigure/src/IO/mitkPlanarFigureWriter.cpp deleted file mode 100644 index ceec5adfb9..0000000000 --- a/Modules/PlanarFigure/src/IO/mitkPlanarFigureWriter.cpp +++ /dev/null @@ -1,294 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkPlanarFigureWriter.h" -#include "mitkBasePropertySerializer.h" -#include "mitkPlaneGeometry.h" -#include - -mitk::PlanarFigureWriter::PlanarFigureWriter() - : m_FileName(""), - m_FilePrefix(""), - m_FilePattern(""), - m_Extension(".pf"), - m_MimeType("application/MITK.PlanarFigure"), - m_Success(false) -{ - this->SetNumberOfRequiredInputs(1); - this->SetNumberOfIndexedOutputs(0); - // this->SetNthOutput( 0, mitk::PlanarFigure::New().GetPointer() ); - - m_CanWriteToMemory = true; -} - -mitk::PlanarFigureWriter::~PlanarFigureWriter() -{ -} - -void mitk::PlanarFigureWriter::GenerateData() -{ - m_Success = false; - - if (!m_WriteToMemory && m_FileName.empty()) - { - MITK_ERROR << "Could not write planar figures. File name is invalid"; - throw std::invalid_argument("file name is empty"); - } - - TiXmlDocument document; - auto decl = new TiXmlDeclaration("1.0", "", ""); // TODO what to write here? encoding? etc.... - document.LinkEndChild(decl); - - auto version = new TiXmlElement("Version"); - version->SetAttribute("Writer", __FILE__); - version->SetAttribute("CVSRevision", "$Revision: 17055 $"); - version->SetAttribute("FileVersion", 1); - document.LinkEndChild(version); - - /* create xml element for each input */ - for (unsigned int i = 0; i < this->GetNumberOfInputs(); ++i) - { - // Create root element for this PlanarFigure - InputType::Pointer pf = this->GetInput(i); - if (pf.IsNull()) - continue; - auto pfElement = new TiXmlElement("PlanarFigure"); - pfElement->SetAttribute("type", pf->GetNameOfClass()); - document.LinkEndChild(pfElement); - - if (pf->GetNumberOfControlPoints() == 0) - continue; - - // PlanarFigure::VertexContainerType* vertices = pf->GetControlPoints(); - // if (vertices == nullptr) - // continue; - - // Serialize property list of PlanarFigure - mitk::PropertyList::Pointer propertyList = pf->GetPropertyList(); - mitk::PropertyList::PropertyMap::const_iterator it; - for (it = propertyList->GetMap()->begin(); it != propertyList->GetMap()->end(); ++it) - { - // Create seralizer for this property - const mitk::BaseProperty *prop = it->second; - std::string serializerName = std::string(prop->GetNameOfClass()) + "Serializer"; - std::list allSerializers = - itk::ObjectFactoryBase::CreateAllInstance(serializerName.c_str()); - - if (allSerializers.size() != 1) - { - // No or too many serializer(s) found, skip this property - continue; - } - - auto *serializer = - dynamic_cast(allSerializers.begin()->GetPointer()); - if (serializer == nullptr) - { - // Serializer not valid; skip this property - } - - auto keyElement = new TiXmlElement("property"); - keyElement->SetAttribute("key", it->first); - keyElement->SetAttribute("type", prop->GetNameOfClass()); - - serializer->SetProperty(prop); - TiXmlElement *valueElement = nullptr; - try - { - valueElement = serializer->Serialize(); - } - catch (...) - { - } - - if (valueElement == nullptr) - { - // Serialization failed; skip this property - continue; - } - - // Add value to property element - keyElement->LinkEndChild(valueElement); - - // Append serialized property to property list - pfElement->LinkEndChild(keyElement); - } - - // Serialize control points of PlanarFigure - auto controlPointsElement = new TiXmlElement("ControlPoints"); - pfElement->LinkEndChild(controlPointsElement); - for (unsigned int i = 0; i < pf->GetNumberOfControlPoints(); i++) - { - auto vElement = new TiXmlElement("Vertex"); - vElement->SetAttribute("id", i); - vElement->SetDoubleAttribute("x", pf->GetControlPoint(i)[0]); - vElement->SetDoubleAttribute("y", pf->GetControlPoint(i)[1]); - controlPointsElement->LinkEndChild(vElement); - } - auto geoElement = new TiXmlElement("Geometry"); - const auto *planeGeo = dynamic_cast(pf->GetPlaneGeometry()); - if (planeGeo != nullptr) - { - // Write parameters of IndexToWorldTransform of the PlaneGeometry - typedef mitk::Geometry3D::TransformType TransformType; - const TransformType *affineGeometry = planeGeo->GetIndexToWorldTransform(); - const TransformType::ParametersType ¶meters = affineGeometry->GetParameters(); - auto vElement = new TiXmlElement("transformParam"); - for (unsigned int i = 0; i < affineGeometry->GetNumberOfParameters(); ++i) - { - std::stringstream paramName; - paramName << "param" << i; - vElement->SetDoubleAttribute(paramName.str().c_str(), parameters.GetElement(i)); - } - geoElement->LinkEndChild(vElement); - - // Write bounds of the PlaneGeometry - typedef mitk::Geometry3D::BoundsArrayType BoundsArrayType; - const BoundsArrayType &bounds = planeGeo->GetBounds(); - vElement = new TiXmlElement("boundsParam"); - for (unsigned int i = 0; i < 6; ++i) - { - std::stringstream boundName; - boundName << "bound" << i; - vElement->SetDoubleAttribute(boundName.str().c_str(), bounds.GetElement(i)); - } - geoElement->LinkEndChild(vElement); - - // Write spacing and origin of the PlaneGeometry - Vector3D spacing = planeGeo->GetSpacing(); - Point3D origin = planeGeo->GetOrigin(); - geoElement->LinkEndChild(this->CreateXMLVectorElement("Spacing", spacing)); - geoElement->LinkEndChild(this->CreateXMLVectorElement("Origin", origin)); - - pfElement->LinkEndChild(geoElement); - } - } - - if (m_WriteToMemory) - { - // Declare a printer - TiXmlPrinter printer; - // attach it to the document you want to convert in to a std::string - document.Accept(&printer); - - // Create memory buffer and print tinyxmldocument there... - m_MemoryBufferSize = printer.Size() + 1; - m_MemoryBuffer = new char[m_MemoryBufferSize]; - strcpy(m_MemoryBuffer, printer.CStr()); - } - else - { - if (document.SaveFile(m_FileName) == false) - { - MITK_ERROR << "Could not write planar figures to " << m_FileName << "\nTinyXML reports '" << document.ErrorDesc() - << "'"; - throw std::ios_base::failure("Error during writing of planar figure xml file."); - } - } - m_Success = true; -} - -void mitk::PlanarFigureWriter::ReleaseMemory() -{ - if (m_MemoryBuffer != nullptr) - { - delete[] m_MemoryBuffer; - } -} - -TiXmlElement *mitk::PlanarFigureWriter::CreateXMLVectorElement(const char *name, itk::FixedArray v) -{ - auto vElement = new TiXmlElement(name); - vElement->SetDoubleAttribute("x", v.GetElement(0)); - vElement->SetDoubleAttribute("y", v.GetElement(1)); - vElement->SetDoubleAttribute("z", v.GetElement(2)); - return vElement; -} - -void mitk::PlanarFigureWriter::ResizeInputs(const unsigned int &num) -{ - // unsigned int prevNum = this->GetNumberOfInputs(); - this->SetNumberOfIndexedInputs(num); - // for ( unsigned int i = prevNum; i < num; ++i ) - //{ - // this->SetNthInput( i, mitk::PlanarFigure::New().GetPointer() ); - //} -} - -void mitk::PlanarFigureWriter::SetInput(InputType *PlanarFigure) -{ - this->ProcessObject::SetNthInput(0, PlanarFigure); -} - -void mitk::PlanarFigureWriter::SetInput(const unsigned int &id, InputType *PlanarFigure) -{ - if (id >= this->GetNumberOfInputs()) - this->ResizeInputs(id + 1); - this->ProcessObject::SetNthInput(id, PlanarFigure); -} - -mitk::PlanarFigure *mitk::PlanarFigureWriter::GetInput() -{ - if (this->GetNumberOfInputs() < 1) - return nullptr; - else - return dynamic_cast(this->GetInput(0)); -} - -mitk::PlanarFigure *mitk::PlanarFigureWriter::GetInput(const unsigned int &num) -{ - return dynamic_cast(this->ProcessObject::GetInput(num)); -} - -bool mitk::PlanarFigureWriter::CanWriteDataType(DataNode *input) -{ - if (input == nullptr) - return false; - - mitk::BaseData *data = input->GetData(); - if (data == nullptr) - return false; - - mitk::PlanarFigure::Pointer PlanarFigure = dynamic_cast(data); - if (PlanarFigure.IsNull()) - return false; - // add code for special subclasses here - return true; -} - -void mitk::PlanarFigureWriter::SetInput(DataNode *input) -{ - if (this->CanWriteDataType(input)) - this->ProcessObject::SetNthInput(0, dynamic_cast(input->GetData())); -} - -std::string mitk::PlanarFigureWriter::GetSupportedBaseData() const -{ - return PlanarFigure::GetStaticNameOfClass(); -} - -std::string mitk::PlanarFigureWriter::GetWritenMIMEType() -{ - return m_MimeType; -} - -std::vector mitk::PlanarFigureWriter::GetPossibleFileExtensions() -{ - std::vector possibleFileExtensions; - possibleFileExtensions.push_back(m_Extension); - return possibleFileExtensions; -} - -std::string mitk::PlanarFigureWriter::GetFileExtension() -{ - return m_Extension; -} diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureWriterFactory.cpp b/Modules/PlanarFigure/src/IO/mitkPlanarFigureWriterFactory.cpp deleted file mode 100644 index 2b1ee14b9e..0000000000 --- a/Modules/PlanarFigure/src/IO/mitkPlanarFigureWriterFactory.cpp +++ /dev/null @@ -1,77 +0,0 @@ -/*============================================================================ - -The Medical Imaging Interaction Toolkit (MITK) - -Copyright (c) German Cancer Research Center (DKFZ) -All rights reserved. - -Use of this source code is governed by a 3-clause BSD license that can be -found in the LICENSE file. - -============================================================================*/ - -#include "mitkPlanarFigureWriterFactory.h" - -#include "itkCreateObjectFunction.h" -#include "itkVersion.h" - -#include - -namespace mitk -{ - template - class CreatePlanarFigureWriter : public itk::CreateObjectFunctionBase - { - public: - /** Standard class typedefs. */ - typedef CreatePlanarFigureWriter Self; - typedef itk::SmartPointer Pointer; - - /** Methods from itk:LightObject. */ - itkFactorylessNewMacro(Self) LightObject::Pointer CreateObject() override - { - typename T::Pointer p = T::New(); - p->Register(); - return p.GetPointer(); - } - - protected: - CreatePlanarFigureWriter() {} - ~CreatePlanarFigureWriter() override {} - private: - CreatePlanarFigureWriter(const Self &); // purposely not implemented - void operator=(const Self &); // purposely not implemented - }; - - PlanarFigureWriterFactory::PlanarFigureWriterFactory() - { - this->RegisterOverride("IOWriter", - "PlanarFigureWriter", - "PlanarFigure xml Writer", - true, - mitk::CreatePlanarFigureWriter::New()); - } - - PlanarFigureWriterFactory::~PlanarFigureWriterFactory() {} - itk::ObjectFactoryBase::Pointer PlanarFigureWriterFactory::GetInstance() - { - static itk::ObjectFactoryBase::Pointer factory(mitk::PlanarFigureWriterFactory::New().GetPointer()); - return factory; - } - - void PlanarFigureWriterFactory::RegisterOneFactory(void) - { - if (GetInstance()->GetReferenceCount() == 1) - { - ObjectFactoryBase::RegisterFactory(GetInstance().GetPointer()); - } - } - - void PlanarFigureWriterFactory::UnRegisterOneFactory(void) - { - ObjectFactoryBase::UnRegisterFactory(GetInstance().GetPointer()); - } - - const char *PlanarFigureWriterFactory::GetITKSourceVersion() const { return ITK_SOURCE_VERSION; } - const char *PlanarFigureWriterFactory::GetDescription() const { return "PlanarFigureWriterFactory"; } -} // end namespace mitk diff --git a/Modules/PlanarFigure/src/IO/mitkPlanarFigureWriterFactory.h b/Modules/PlanarFigure/src/IO/mitkPlanarFigureWriterFactory.h deleted file mode 100644 index b0f8d30e96..0000000000 --- a/Modules/PlanarFigure/src/IO/mitkPlanarFigureWriterFactory.h +++ /dev/null @@ -1,58 +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 PLANARFIGURE_WRITERFACTORY_H_HEADER_INCLUDED -#define PLANARFIGURE_WRITERFACTORY_H_HEADER_INCLUDED - -#include "itkObjectFactoryBase.h" -#include "mitkBaseData.h" - -namespace mitk -{ - class PlanarFigureWriterFactory : public itk::ObjectFactoryBase - { - public: - mitkClassMacroItkParent(mitk::PlanarFigureWriterFactory, itk::ObjectFactoryBase); - - /** Class methods used to interface with the registered factories. */ - const char *GetITKSourceVersion(void) const override; - const char *GetDescription(void) const override; - - /** Method for class instantiation. */ - itkFactorylessNewMacro(Self); - - /** - * Register one factory of this type - * \deprecatedSince{2013_09} - */ - DEPRECATED(static void RegisterOneFactory(void)); - - /** - * UnRegister one factory of this type - * \deprecatedSince{2013_09} - */ - DEPRECATED(static void UnRegisterOneFactory(void)); - - protected: - PlanarFigureWriterFactory(); - ~PlanarFigureWriterFactory() override; - - private: - PlanarFigureWriterFactory(const Self &); // purposely not implemented - void operator=(const Self &); // purposely not implemented - - static itk::ObjectFactoryBase::Pointer GetInstance(); - }; - -} // end namespace mitk - -#endif // PLANARFIGURE_WRITERFACTORY_H_HEADER_INCLUDED diff --git a/Modules/PlanarFigure/test/mitkPlanarFigureIOTest.cpp b/Modules/PlanarFigure/test/mitkPlanarFigureIOTest.cpp index e24ed0bfef..40fd836151 100644 --- a/Modules/PlanarFigure/test/mitkPlanarFigureIOTest.cpp +++ b/Modules/PlanarFigure/test/mitkPlanarFigureIOTest.cpp @@ -1,592 +1,510 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestingMacros.h" #include "mitkPlanarAngle.h" #include "mitkPlanarCircle.h" #include "mitkPlanarCross.h" #include "mitkPlanarFourPointAngle.h" #include "mitkPlanarLine.h" #include "mitkPlanarPolygon.h" #include "mitkPlanarRectangle.h" #include "mitkPlanarSubdivisionPolygon.h" -#include "mitkPlanarFigureReader.h" -#include "mitkPlanarFigureWriter.h" - #include "mitkPlaneGeometry.h" -#include +#include "mitkGeometry3D.h" +#include "mitkAbstractFileIO.h" +#include "mitkFileReaderRegistry.h" +#include "mitkFileWriterRegistry.h" +#include "mitkIOUtil.h" -static mitk::PlanarFigure::Pointer Clone(mitk::PlanarFigure::Pointer original) -{ - return original->Clone(); -} +#include /** \brief Helper class for testing PlanarFigure reader and writer classes. */ class PlanarFigureIOTestClass { public: - typedef std::list PlanarFigureList; - typedef std::vector PlanarFigureToMemoryWriterList; + typedef std::map PlanarFigureMap; + typedef std::map PlanarFigureToStreamMap; - static PlanarFigureList CreatePlanarFigures() + static PlanarFigureMap CreatePlanarFigures() { - PlanarFigureList planarFigures; + PlanarFigureMap planarFigures; // Create PlaneGeometry on which to place the PlanarFigures mitk::PlaneGeometry::Pointer planeGeometry = mitk::PlaneGeometry::New(); planeGeometry->InitializeStandardPlane(100.0, 100.0); // Create a few sample points for PlanarFigure placement mitk::Point2D p0; p0[0] = 20.0; p0[1] = 20.0; mitk::Point2D p1; p1[0] = 80.0; p1[1] = 80.0; mitk::Point2D p2; p2[0] = 90.0; p2[1] = 10.0; mitk::Point2D p3; p3[0] = 10.0; p3[1] = 90.0; // Create PlanarAngle mitk::PlanarAngle::Pointer planarAngle = mitk::PlanarAngle::New(); planarAngle->SetPlaneGeometry(planeGeometry); planarAngle->PlaceFigure(p0); planarAngle->SetCurrentControlPoint(p1); planarAngle->AddControlPoint(p2); planarAngle->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarAngle.GetPointer()); + planarFigures.emplace("planarAngle",planarAngle.GetPointer()); // Create PlanarCircle mitk::PlanarCircle::Pointer planarCircle = mitk::PlanarCircle::New(); planarCircle->SetPlaneGeometry(planeGeometry); planarCircle->PlaceFigure(p0); planarCircle->SetCurrentControlPoint(p1); planarCircle->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarCircle.GetPointer()); + planarFigures.emplace("planarCircle",planarCircle.GetPointer()); // Create PlanarCross mitk::PlanarCross::Pointer planarCross = mitk::PlanarCross::New(); planarCross->SetSingleLineMode(false); planarCross->SetPlaneGeometry(planeGeometry); planarCross->PlaceFigure(p0); planarCross->SetCurrentControlPoint(p1); planarCross->AddControlPoint(p2); planarCross->AddControlPoint(p3); planarCross->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarCross.GetPointer()); + planarFigures.emplace("planarCross",planarCross.GetPointer()); // Create PlanarFourPointAngle mitk::PlanarFourPointAngle::Pointer planarFourPointAngle = mitk::PlanarFourPointAngle::New(); planarFourPointAngle->SetPlaneGeometry(planeGeometry); planarFourPointAngle->PlaceFigure(p0); planarFourPointAngle->SetCurrentControlPoint(p1); planarFourPointAngle->AddControlPoint(p2); planarFourPointAngle->AddControlPoint(p3); planarFourPointAngle->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarFourPointAngle.GetPointer()); + planarFigures.emplace("planarFourPointAngle",planarFourPointAngle.GetPointer()); // Create PlanarLine mitk::PlanarLine::Pointer planarLine = mitk::PlanarLine::New(); planarLine->SetPlaneGeometry(planeGeometry); planarLine->PlaceFigure(p0); planarLine->SetCurrentControlPoint(p1); planarLine->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarLine.GetPointer()); + planarFigures.emplace("planarLine",planarLine.GetPointer()); // Create PlanarPolygon mitk::PlanarPolygon::Pointer planarPolygon = mitk::PlanarPolygon::New(); planarPolygon->SetClosed(false); planarPolygon->SetPlaneGeometry(planeGeometry); planarPolygon->PlaceFigure(p0); planarPolygon->SetCurrentControlPoint(p1); planarPolygon->AddControlPoint(p2); planarPolygon->AddControlPoint(p3); planarPolygon->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarPolygon.GetPointer()); + planarFigures.emplace("planarPolygon",planarPolygon.GetPointer()); // Create PlanarSubdivisionPolygon mitk::PlanarSubdivisionPolygon::Pointer planarSubdivisionPolygon = mitk::PlanarSubdivisionPolygon::New(); planarSubdivisionPolygon->SetClosed(false); planarSubdivisionPolygon->SetPlaneGeometry(planeGeometry); planarSubdivisionPolygon->PlaceFigure(p0); planarSubdivisionPolygon->SetCurrentControlPoint(p1); planarSubdivisionPolygon->AddControlPoint(p2); planarSubdivisionPolygon->AddControlPoint(p3); planarSubdivisionPolygon->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarSubdivisionPolygon.GetPointer()); + planarFigures.emplace("planarSubdivisionPolygon",planarSubdivisionPolygon.GetPointer()); // Create PlanarRectangle mitk::PlanarRectangle::Pointer planarRectangle = mitk::PlanarRectangle::New(); planarRectangle->SetPlaneGeometry(planeGeometry); planarRectangle->PlaceFigure(p0); planarRectangle->SetCurrentControlPoint(p1); planarRectangle->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarRectangle.GetPointer()); + planarFigures.emplace("planarRectangle",planarRectangle.GetPointer()); // create preciseGeometry which is using float coordinates mitk::PlaneGeometry::Pointer preciseGeometry = mitk::PlaneGeometry::New(); mitk::Vector3D right; right[0] = 0.0; right[1] = 1.23456; right[2] = 0.0; mitk::Vector3D down; down[0] = 1.23456; down[1] = 0.0; down[2] = 0.0; mitk::Vector3D spacing; spacing[0] = 0.0123456; spacing[1] = 0.0123456; spacing[2] = 1.123456; preciseGeometry->InitializeStandardPlane(right, down, &spacing); // convert points into the precise coordinates mitk::Point2D p0precise; p0precise[0] = p0[0] * spacing[0]; p0precise[1] = p0[1] * spacing[1]; mitk::Point2D p1precise; p1precise[0] = p1[0] * spacing[0]; p1precise[1] = p1[1] * spacing[1]; mitk::Point2D p2precise; p2precise[0] = p2[0] * spacing[0]; p2precise[1] = p2[1] * spacing[1]; mitk::Point2D p3precise; p3precise[0] = p3[0] * spacing[0]; p3precise[1] = p3[1] * spacing[1]; // Now all PlanarFigures are create using the precise Geometry // Create PlanarCross mitk::PlanarCross::Pointer nochncross = mitk::PlanarCross::New(); nochncross->SetSingleLineMode(false); nochncross->SetPlaneGeometry(preciseGeometry); nochncross->PlaceFigure(p0precise); nochncross->SetCurrentControlPoint(p1precise); nochncross->AddControlPoint(p2precise); nochncross->AddControlPoint(p3precise); nochncross->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(nochncross.GetPointer()); + planarFigures.emplace("nochncross", nochncross.GetPointer()); // Create PlanarAngle mitk::PlanarAngle::Pointer planarAnglePrecise = mitk::PlanarAngle::New(); planarAnglePrecise->SetPlaneGeometry(preciseGeometry); planarAnglePrecise->PlaceFigure(p0precise); planarAnglePrecise->SetCurrentControlPoint(p1precise); planarAnglePrecise->AddControlPoint(p2precise); planarAnglePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarAnglePrecise.GetPointer()); + planarFigures.emplace("planarAnglePrecise",planarAnglePrecise.GetPointer()); // Create PlanarCircle mitk::PlanarCircle::Pointer planarCirclePrecise = mitk::PlanarCircle::New(); planarCirclePrecise->SetPlaneGeometry(preciseGeometry); planarCirclePrecise->PlaceFigure(p0precise); planarCirclePrecise->SetCurrentControlPoint(p1precise); planarCirclePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarCirclePrecise.GetPointer()); + planarFigures.emplace("planarCirclePrecise",planarCirclePrecise.GetPointer()); // Create PlanarFourPointAngle mitk::PlanarFourPointAngle::Pointer planarFourPointAnglePrecise = mitk::PlanarFourPointAngle::New(); planarFourPointAnglePrecise->SetPlaneGeometry(preciseGeometry); planarFourPointAnglePrecise->PlaceFigure(p0precise); planarFourPointAnglePrecise->SetCurrentControlPoint(p1precise); planarFourPointAnglePrecise->AddControlPoint(p2precise); planarFourPointAnglePrecise->AddControlPoint(p3precise); planarFourPointAnglePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarFourPointAnglePrecise.GetPointer()); + planarFigures.emplace("planarFourPointAnglePrecise",planarFourPointAnglePrecise.GetPointer()); // Create PlanarLine mitk::PlanarLine::Pointer planarLinePrecise = mitk::PlanarLine::New(); planarLinePrecise->SetPlaneGeometry(preciseGeometry); planarLinePrecise->PlaceFigure(p0precise); planarLinePrecise->SetCurrentControlPoint(p1precise); planarLinePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarLinePrecise.GetPointer()); + planarFigures.emplace("planarLinePrecise",planarLinePrecise.GetPointer()); // Create PlanarPolygon mitk::PlanarPolygon::Pointer planarPolygonPrecise = mitk::PlanarPolygon::New(); planarPolygonPrecise->SetClosed(false); planarPolygonPrecise->SetPlaneGeometry(preciseGeometry); planarPolygonPrecise->PlaceFigure(p0precise); planarPolygonPrecise->SetCurrentControlPoint(p1precise); planarPolygonPrecise->AddControlPoint(p2precise); planarPolygonPrecise->AddControlPoint(p3precise); planarPolygonPrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarPolygonPrecise.GetPointer()); + planarFigures.emplace("planarPolygonPrecise",planarPolygonPrecise.GetPointer()); // Create PlanarSubdivisionPolygon mitk::PlanarSubdivisionPolygon::Pointer planarSubdivisionPolygonPrecise = mitk::PlanarSubdivisionPolygon::New(); planarSubdivisionPolygonPrecise->SetClosed(false); planarSubdivisionPolygonPrecise->SetPlaneGeometry(preciseGeometry); planarSubdivisionPolygonPrecise->PlaceFigure(p0precise); planarSubdivisionPolygonPrecise->SetCurrentControlPoint(p1precise); planarSubdivisionPolygonPrecise->AddControlPoint(p2precise); planarSubdivisionPolygonPrecise->AddControlPoint(p3precise); planarSubdivisionPolygonPrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarSubdivisionPolygonPrecise.GetPointer()); + planarFigures.emplace("planarSubdivisionPolygonPrecise",planarSubdivisionPolygonPrecise.GetPointer()); // Create PlanarRectangle mitk::PlanarRectangle::Pointer planarRectanglePrecise = mitk::PlanarRectangle::New(); planarRectanglePrecise->SetPlaneGeometry(preciseGeometry); planarRectanglePrecise->PlaceFigure(p0precise); planarRectanglePrecise->SetCurrentControlPoint(p1precise); planarRectanglePrecise->GetPropertyList()->SetBoolProperty("initiallyplaced", true); - planarFigures.push_back(planarRectanglePrecise.GetPointer()); + planarFigures.emplace("planarRectanglePrecise",planarRectanglePrecise.GetPointer()); return planarFigures; } - static PlanarFigureList CreateDeepCopiedPlanarFigures(PlanarFigureList original) + static PlanarFigureMap CreateClonedPlanarFigures(PlanarFigureMap original) { - PlanarFigureList copiedPlanarFigures; - - PlanarFigureList::iterator it1; + PlanarFigureMap copiedPlanarFigures; - for (it1 = original.begin(); it1 != original.end(); ++it1) + for (const auto& pf : original) { - mitk::PlanarFigure::Pointer copiedFigure = (*it1)->Clone(); + mitk::PlanarFigure::Pointer copiedFigure = pf.second->Clone(); - copiedPlanarFigures.push_back(copiedFigure); + copiedPlanarFigures[pf.first] = copiedFigure; } return copiedPlanarFigures; } - static PlanarFigureList CreateClonedPlanarFigures(PlanarFigureList original) - { - PlanarFigureList clonedPlanarFigures; - clonedPlanarFigures.resize(original.size()); - std::transform(original.begin(), original.end(), clonedPlanarFigures.begin(), Clone); - return clonedPlanarFigures; - } - - static void VerifyPlanarFigures(PlanarFigureList &planarFigures1, PlanarFigureList &planarFigures2) + static void VerifyPlanarFigures(PlanarFigureMap &referencePfs, PlanarFigureMap &testPfs) { - PlanarFigureList::iterator it1, it2; + PlanarFigureMap::iterator it1, it2; int i = 0; - for (it1 = planarFigures1.begin(); it1 != planarFigures1.end(); ++it1) + for (it1 = referencePfs.begin(); it1 != referencePfs.end(); ++it1) { bool planarFigureFound = false; int j = 0; - for (it2 = planarFigures2.begin(); it2 != planarFigures2.end(); ++it2) + for (it2 = testPfs.begin(); it2 != testPfs.end(); ++it2) { // Compare PlanarFigures (returns false if different types) - if (ComparePlanarFigures(*it1, *it2)) + if (ComparePlanarFigures(it1->second, it2->second)) { planarFigureFound = true; } ++j; } // Test if (at least) on PlanarFigure of the first type was found in the second list MITK_TEST_CONDITION_REQUIRED(planarFigureFound, - "Testing if " << (*it1)->GetNameOfClass() << " has a counterpart " << i); + "Testing if " << it1->second->GetNameOfClass() << " has a counterpart " << i); ++i; } } - static bool ComparePlanarFigures(mitk::PlanarFigure *figure1, mitk::PlanarFigure *figure2) + static bool ComparePlanarFigures(const mitk::PlanarFigure *referencePf, const mitk::PlanarFigure *testPf) { // Test if PlanarFigures are of same type; otherwise return - if (strcmp(figure1->GetNameOfClass(), figure2->GetNameOfClass()) != 0) + if (strcmp(referencePf->GetNameOfClass(), testPf->GetNameOfClass()) != 0) { return false; } - if (strcmp(figure1->GetNameOfClass(), "PlanarCross") == 0) + if (strcmp(referencePf->GetNameOfClass(), "PlanarCross") == 0) { std::cout << "Planar Cross Found" << std::endl; } // Test for equal number of control points - if (figure1->GetNumberOfControlPoints() != figure2->GetNumberOfControlPoints()) + if (referencePf->GetNumberOfControlPoints() != testPf->GetNumberOfControlPoints()) { return false; } // Test if all control points are equal - for (unsigned int i = 0; i < figure1->GetNumberOfControlPoints(); ++i) + for (unsigned int i = 0; i < referencePf->GetNumberOfControlPoints(); ++i) { - mitk::Point2D point1 = figure1->GetControlPoint(i); - mitk::Point2D point2 = figure2->GetControlPoint(i); + mitk::Point2D point1 = referencePf->GetControlPoint(i); + mitk::Point2D point2 = testPf->GetControlPoint(i); if (point1.EuclideanDistanceTo(point2) >= mitk::eps) { return false; } } // Test for equal number of properties typedef mitk::PropertyList::PropertyMap PropertyMap; - const PropertyMap *properties1 = figure1->GetPropertyList()->GetMap(); - const PropertyMap *properties2 = figure2->GetPropertyList()->GetMap(); - - if (properties1->size() != properties2->size()) - { - return false; - } + const PropertyMap *refProperties = referencePf->GetPropertyList()->GetMap(); + const PropertyMap *testProperties = testPf->GetPropertyList()->GetMap(); MITK_INFO << "List 1:"; - for (auto i1 = properties1->begin(); i1 != properties1->end(); ++i1) + for (auto i1 = refProperties->begin(); i1 != refProperties->end(); ++i1) { std::cout << i1->first << std::endl; } MITK_INFO << "List 2:"; - for (auto i2 = properties2->begin(); i2 != properties2->end(); ++i2) + for (auto i2 = testProperties->begin(); i2 != testProperties->end(); ++i2) { std::cout << i2->first << std::endl; } MITK_INFO << "-------"; - // Test if all properties are equal - if (!std::equal(properties1->begin(), properties1->end(), properties2->begin(), PropertyMapEntryCompare())) + //remark test planar figures may have additional properties + //(e.g. reader meta information), but they are not relevant + //for the test. Only check of all properties of the reference + //are present and correct. + for (const auto prop : *refProperties) { - return false; + auto finding = testProperties->find(prop.first); + if (finding == testProperties->end()) + { + return false; + } + + MITK_INFO << "Comparing " << prop.first << "(" << prop.second->GetValueAsString() << ") and " << finding->first + << "(" << finding->second->GetValueAsString() << ")"; + // Compare property objects contained in the map entries (see mitk::PropertyList) + if (!(*(prop.second) == *(finding->second))) return false; } // Test if Geometry is equal - const auto *planeGeometry1 = dynamic_cast(figure1->GetPlaneGeometry()); - const auto *planeGeometry2 = dynamic_cast(figure2->GetPlaneGeometry()); + const auto *planeGeometry1 = dynamic_cast(referencePf->GetPlaneGeometry()); + const auto *planeGeometry2 = dynamic_cast(testPf->GetPlaneGeometry()); // Test Geometry transform parameters typedef mitk::Geometry3D::TransformType TransformType; const TransformType *affineGeometry1 = planeGeometry1->GetIndexToWorldTransform(); const TransformType::ParametersType ¶meters1 = affineGeometry1->GetParameters(); const TransformType::ParametersType ¶meters2 = planeGeometry2->GetIndexToWorldTransform()->GetParameters(); for (unsigned int i = 0; i < affineGeometry1->GetNumberOfParameters(); ++i) { if (fabs(parameters1.GetElement(i) - parameters2.GetElement(i)) >= mitk::eps) { return false; } } // Test Geometry bounds typedef mitk::Geometry3D::BoundsArrayType BoundsArrayType; const BoundsArrayType &bounds1 = planeGeometry1->GetBounds(); const BoundsArrayType &bounds2 = planeGeometry2->GetBounds(); for (unsigned int i = 0; i < 6; ++i) { if (fabs(bounds1.GetElement(i) - bounds2.GetElement(i)) >= mitk::eps) { return false; }; } // Test Geometry spacing and origin mitk::Vector3D spacing1 = planeGeometry1->GetSpacing(); mitk::Vector3D spacing2 = planeGeometry2->GetSpacing(); if ((spacing1 - spacing2).GetNorm() >= mitk::eps) { return false; } mitk::Point3D origin1 = planeGeometry1->GetOrigin(); mitk::Point3D origin2 = planeGeometry2->GetOrigin(); if (origin1.EuclideanDistanceTo(origin2) >= mitk::eps) { return false; } return true; } - static void SerializePlanarFigures(PlanarFigureList &planarFigures, std::string &fileName) + static PlanarFigureToStreamMap SerializePlanarFiguresToMemoryBuffers(PlanarFigureMap &planarFigures) { - // std::string sceneFileName = Poco::Path::temp() + /*Poco::Path::separator() +*/ "scene.zip"; - std::cout << "File name: " << fileName << std::endl; - - mitk::PlanarFigureWriter::Pointer writer = mitk::PlanarFigureWriter::New(); - writer->SetFileName(fileName.c_str()); + PlanarFigureToStreamMap pfMemoryStreams; - unsigned int i; - PlanarFigureList::iterator it; - for (it = planarFigures.begin(), i = 0; it != planarFigures.end(); ++it, ++i) + for (const auto& pf : planarFigures) { - writer->SetInput(i, *it); + mitk::FileWriterRegistry writerRegistry; + auto writers = writerRegistry.GetWriters(pf.second.GetPointer(), ""); + + std::ostringstream stream; + writers[0]->SetOutputStream("",&stream); + writers[0]->SetInput(pf.second); + writers[0]->Write(); + pfMemoryStreams.emplace(pf.first, stream.str()); } - writer->Update(); - - MITK_TEST_CONDITION_REQUIRED(writer->GetSuccess(), "Testing if writing was successful"); + return pfMemoryStreams; } - static PlanarFigureList DeserializePlanarFigures(std::string &fileName) + static PlanarFigureMap DeserializePlanarFiguresFromMemoryBuffers(PlanarFigureToStreamMap pfMemoryStreams) { - // Read in the planar figures - mitk::PlanarFigureReader::Pointer reader = mitk::PlanarFigureReader::New(); - reader->SetFileName(fileName.c_str()); - reader->Update(); - - MITK_TEST_CONDITION_REQUIRED(reader->GetSuccess(), "Testing if reading was successful"); - // Store them in the list and return it - PlanarFigureList planarFigures; - for (unsigned int i = 0; i < reader->GetNumberOfOutputs(); ++i) - { - mitk::PlanarFigure *figure = reader->GetOutput(i); - planarFigures.push_back(figure); - } + PlanarFigureMap planarFigures; - return planarFigures; - } + mitk::FileReaderRegistry readerRegistry; + std::vector readers = + readerRegistry.GetReaders(mitk::FileReaderRegistry::GetMimeTypeForFile("pf")); - static PlanarFigureToMemoryWriterList SerializePlanarFiguresToMemoryBuffers(PlanarFigureList &planarFigures) - { - PlanarFigureToMemoryWriterList pfMemoryWriters; - unsigned int i; - PlanarFigureList::iterator it; - - bool success = true; - for (it = planarFigures.begin(), i = 0; it != planarFigures.end(); ++it, ++i) - { - mitk::PlanarFigureWriter::Pointer writer = mitk::PlanarFigureWriter::New(); - writer->SetWriteToMemory(true); - writer->SetInput(*it); - writer->Update(); - - pfMemoryWriters.push_back(writer); - - if (!writer->GetSuccess()) - success = false; - } - - MITK_TEST_CONDITION_REQUIRED(success, "Testing if writing to memory buffers was successful"); - - return pfMemoryWriters; - } - - static PlanarFigureList DeserializePlanarFiguresFromMemoryBuffers(PlanarFigureToMemoryWriterList pfMemoryWriters) - { - // Store them in the list and return it - PlanarFigureList planarFigures; - bool success = true; - for (unsigned int i = 0; i < pfMemoryWriters.size(); ++i) + for (const auto& pfStream : pfMemoryStreams) { - // Read in the planar figures - mitk::PlanarFigureReader::Pointer reader = mitk::PlanarFigureReader::New(); - reader->SetReadFromMemory(true); - reader->SetMemoryBuffer(pfMemoryWriters[i]->GetMemoryPointer(), pfMemoryWriters[i]->GetMemorySize()); - reader->Update(); - mitk::PlanarFigure *figure = reader->GetOutput(0); - planarFigures.push_back(figure); - - if (!reader->GetSuccess()) - success = false; + std::istringstream stream; + stream.str(pfStream.second); + readers[0]->SetInput("", &stream); + auto pfRead = readers[0]->Read(); + MITK_TEST_CONDITION(pfRead.size() == 1, "One planar figure should be read from stream."); + auto pf = dynamic_cast(pfRead.front().GetPointer()); + MITK_TEST_CONDITION(pf != nullptr, "Loaded data should be a planar figure."); + planarFigures.emplace(pfStream.first, pf); } - MITK_TEST_CONDITION_REQUIRED(success, "Testing if reading was successful"); - return planarFigures; } -private: - class PropertyMapEntryCompare - { - public: - bool operator()(const mitk::PropertyList::PropertyMap::value_type &entry1, - const mitk::PropertyList::PropertyMap::value_type &entry2) - { - MITK_INFO << "Comparing " << entry1.first << "(" << entry1.second->GetValueAsString() << ") and " << entry2.first - << "(" << entry2.second->GetValueAsString() << ")"; - // Compare property objects contained in the map entries (see mitk::PropertyList) - return *(entry1.second) == *(entry2.second); - } - }; - }; // end test helper class /** \brief Test for PlanarFigure reader and writer classes. * * The test works as follows: * * First, a number of PlanarFigure objects of different types are created and placed with * various control points. These objects are the serialized to file, read again from file, and * the retrieved objects are compared with their control points, properties, and geometry * information to the original PlanarFigure objects. */ int mitkPlanarFigureIOTest(int /* argc */, char * /*argv*/ []) { MITK_TEST_BEGIN("PlanarFigureIO"); // Create a number of PlanarFigure objects - PlanarFigureIOTestClass::PlanarFigureList originalPlanarFigures = PlanarFigureIOTestClass::CreatePlanarFigures(); - - // Create a number of "deep-copied" planar figures to test the DeepCopy function (deprecated) - PlanarFigureIOTestClass::PlanarFigureList copiedPlanarFigures = - PlanarFigureIOTestClass::CreateDeepCopiedPlanarFigures(originalPlanarFigures); - - PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, copiedPlanarFigures); + PlanarFigureIOTestClass::PlanarFigureMap originalPlanarFigures = PlanarFigureIOTestClass::CreatePlanarFigures(); // Create a number of cloned planar figures to test the Clone function - PlanarFigureIOTestClass::PlanarFigureList clonedPlanarFigures = + PlanarFigureIOTestClass::PlanarFigureMap clonedPlanarFigures = PlanarFigureIOTestClass::CreateClonedPlanarFigures(originalPlanarFigures); PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, clonedPlanarFigures); - // Write PlanarFigure objects into temp file - // tmpname - static unsigned long count = 0; - unsigned long n = count++; - std::ostringstream name; - for (int i = 0; i < 6; ++i) + + std::map pfFileNameMap; + for (const auto& pf : originalPlanarFigures) { - name << char('a' + (n % 26)); - n /= 26; + std::string filename = mitk::IOUtil::CreateTemporaryFile(pf.first+"_XXXXXX.pf", itksys::SystemTools::GetCurrentWorkingDirectory()); + mitk::IOUtil::Save(pf.second, filename); + pfFileNameMap.emplace(pf.first, filename); } - std::string myname; - myname.append(name.str()); - - std::string fileName = itksys::SystemTools::GetCurrentWorkingDirectory() + myname + ".pf"; - - PlanarFigureIOTestClass::SerializePlanarFigures(originalPlanarFigures, fileName); // Write PlanarFigure objects to memory buffers - PlanarFigureIOTestClass::PlanarFigureToMemoryWriterList writersWithMemoryBuffers = + PlanarFigureIOTestClass::PlanarFigureToStreamMap writersStreams = PlanarFigureIOTestClass::SerializePlanarFiguresToMemoryBuffers(originalPlanarFigures); // Read PlanarFigure objects from temp file - PlanarFigureIOTestClass::PlanarFigureList retrievedPlanarFigures = - PlanarFigureIOTestClass::DeserializePlanarFigures(fileName); - - // Read PlanarFigure objects from memory buffers - PlanarFigureIOTestClass::PlanarFigureList retrievedPlanarFiguresFromMemory = - PlanarFigureIOTestClass::DeserializePlanarFiguresFromMemoryBuffers(writersWithMemoryBuffers); - - auto it = writersWithMemoryBuffers.begin(); - while (it != writersWithMemoryBuffers.end()) + PlanarFigureIOTestClass::PlanarFigureMap retrievedPlanarFigures; + for (const auto& files : pfFileNameMap) { - (*it)->ReleaseMemory(); - ++it; + auto pf = mitk::IOUtil::Load(files.second); + retrievedPlanarFigures.emplace(files.first, pf); } + // Read PlanarFigure objects from memory buffers + PlanarFigureIOTestClass::PlanarFigureMap retrievedPlanarFiguresFromMemory = + PlanarFigureIOTestClass::DeserializePlanarFiguresFromMemoryBuffers(writersStreams); + // Test if original and retrieved PlanarFigure objects are the same PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, retrievedPlanarFigures); // Test if original and memory retrieved PlanarFigure objects are the same PlanarFigureIOTestClass::VerifyPlanarFigures(originalPlanarFigures, retrievedPlanarFiguresFromMemory); // empty the originalPlanarFigures originalPlanarFigures.clear(); - // Test if deep-copied and retrieved PlanarFigure objects are the same - PlanarFigureIOTestClass::VerifyPlanarFigures(copiedPlanarFigures, retrievedPlanarFigures); + // Test if cloned and retrieved PlanarFigure objects are the same + PlanarFigureIOTestClass::VerifyPlanarFigures(clonedPlanarFigures, retrievedPlanarFigures); MITK_TEST_END() } diff --git a/Modules/PlanarFigure/test/mitkPlanarFigureInteractionTest.cpp b/Modules/PlanarFigure/test/mitkPlanarFigureInteractionTest.cpp index ed99c4df76..66852cd761 100644 --- a/Modules/PlanarFigure/test/mitkPlanarFigureInteractionTest.cpp +++ b/Modules/PlanarFigure/test/mitkPlanarFigureInteractionTest.cpp @@ -1,202 +1,197 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestingMacros.h" #include #include #include #include #include #include -#include -#include #include #include #include #include #include #include #include #include #include #include #include #include #include "usModuleRegistry.h" class mitkPlanarFigureInteractionTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPlanarFigureInteractionTestSuite); MITK_TEST(AngleInteractionCreate); MITK_TEST(Angle2InteractionCreate); MITK_TEST(BezierCurveInteractionCreate); MITK_TEST(CircleInteractionCreate); MITK_TEST(DoubleEllipseInteractionCreate); MITK_TEST(PlanarFourPointAngleInteractionCreate); MITK_TEST(PlanarLineInteractionCreate); MITK_TEST(PlanarPolygonInteractionCreate); MITK_TEST(NonClosedPlanarPolygonInteractionCreate); MITK_TEST(RectangleInteractionCreate); // BUG 19304 // MITK_TEST(PlanarSubdivisionInteractionCreate); CPPUNIT_TEST_SUITE_END(); public: void setUp() { /// \todo Fix leaks of vtkObjects. Bug 18095. vtkDebugLeaks::SetExitError(0); } void tearDown() {} void RunTest(mitk::PlanarFigure::Pointer figure, std::string interactionXmlPath, std::string referenceFigurePath) { mitk::DataNode::Pointer node; mitk::PlanarFigureInteractor::Pointer figureInteractor; // Create DataNode as a container for our PlanarFigure node = mitk::DataNode::New(); node->SetData(figure); mitk::InteractionTestHelper interactionTestHelper(GetTestDataFilePath(interactionXmlPath)); // Load a bounding image mitk::Image::Pointer testImage = mitk::IOUtil::Load(GetTestDataFilePath("Pic3D.nrrd")); figure->SetGeometry(testImage->GetGeometry()); mitk::DataNode::Pointer dn = mitk::DataNode::New(); dn->SetData(testImage); interactionTestHelper.AddNodeToStorage(dn); interactionTestHelper.GetDataStorage()->Add(node, dn); node->SetName("PLANAR FIGURE"); // set as selected node->SetSelected(true); node->AddProperty("selected", mitk::BoolProperty::New(true)); // Load state machine figureInteractor = mitk::PlanarFigureInteractor::New(); us::Module *planarFigureModule = us::ModuleRegistry::GetModule("MitkPlanarFigure"); figureInteractor->LoadStateMachine("PlanarFigureInteraction.xml", planarFigureModule); figureInteractor->SetEventConfig("PlanarFigureConfig.xml", planarFigureModule); figureInteractor->SetDataNode(node); // Start Interaction interactionTestHelper.PlaybackInteraction(); // Load reference PlanarFigure - mitk::PlanarFigureReader::Pointer reader = mitk::PlanarFigureReader::New(); - reader->SetFileName(GetTestDataFilePath(referenceFigurePath)); - reader->Update(); - mitk::PlanarFigure::Pointer reference = reader->GetOutput(0); + auto reference = mitk::IOUtil::Load(GetTestDataFilePath(referenceFigurePath)); // Compare figures MITK_ASSERT_EQUAL(figure, reference, "Compare figure with reference"); } void AngleInteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarAngle::New(); RunTest(figure, "InteractionTestData/Interactions/Angle1.xml", "InteractionTestData/ReferenceData/Angle1.pf"); } void Angle2InteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarAngle::New(); RunTest(figure, "InteractionTestData/Interactions/Angle2.xml", "InteractionTestData/ReferenceData/Angle2.pf"); } void BezierCurveInteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarBezierCurve::New(); RunTest(figure, "InteractionTestData/Interactions/Bezier.xml", "InteractionTestData/ReferenceData/Bezier.pf"); } void CircleInteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarCircle::New(); RunTest(figure, "InteractionTestData/Interactions/Circle.xml", "InteractionTestData/ReferenceData/Circle.pf"); } void DoubleEllipseInteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarDoubleEllipse::New(); RunTest(figure, "InteractionTestData/Interactions/DoubleEllipse.xml", "InteractionTestData/ReferenceData/DoubleEllipse.pf"); } void PlanarSubdivisionInteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarSubdivisionPolygon::New(); RunTest(figure, "InteractionTestData/Interactions/SubdivisionPolygon.xml", "InteractionTestData/ReferenceData/SubDivision.pf"); } void PlanarFourPointAngleInteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarFourPointAngle::New(); RunTest(figure, "InteractionTestData/Interactions/Planar4PointAngle.xml", "InteractionTestData/ReferenceData/Planar4PointAngle.pf"); } void PlanarLineInteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarLine::New(); RunTest(figure, "InteractionTestData/Interactions/Line.xml", "InteractionTestData/ReferenceData/Line.pf"); } void PlanarPolygonInteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarPolygon::New(); RunTest(figure, "InteractionTestData/Interactions/Polygon.xml", "InteractionTestData/ReferenceData/Polygon.pf"); } void NonClosedPlanarPolygonInteractionCreate() { mitk::PlanarPolygon::Pointer figure; figure = mitk::PlanarPolygon::New(); figure->ClosedOff(); RunTest( figure.GetPointer(), "InteractionTestData/Interactions/Path.xml", "InteractionTestData/ReferenceData/Path.pf"); } void RectangleInteractionCreate() { mitk::PlanarFigure::Pointer figure; figure = mitk::PlanarRectangle::New(); RunTest(figure, "InteractionTestData/Interactions/Rectangle.xml", "InteractionTestData/ReferenceData/Rectangle.pf"); } // this is only for the OpenGL check mitkPlanarFigureInteractionTestSuite() : m_RenderingTestHelper(300, 300) {} private: mitk::RenderingTestHelper m_RenderingTestHelper; }; MITK_TEST_SUITE_REGISTRATION(mitkPlanarFigureInteraction) diff --git a/Modules/Python/autoload/PythonService/mitkPythonService.cpp b/Modules/Python/autoload/PythonService/mitkPythonService.cpp index de6dafd9d7..4dd7ae1c18 100644 --- a/Modules/Python/autoload/PythonService/mitkPythonService.cpp +++ b/Modules/Python/autoload/PythonService/mitkPythonService.cpp @@ -1,906 +1,917 @@ /*============================================================================ 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 "mitkPythonService.h" #include #include #include #include + +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 5208) +#endif + #include + +#ifdef _MSC_VER +# pragma warning(pop) +#endif + #include "PythonPath.h" #include #include #include #include #include #include #include #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION #include #include #ifndef WIN32 #include #endif typedef itksys::SystemTools ist; mitk::PythonService::PythonService() : m_ItkWrappingAvailable( true ) , m_OpenCVWrappingAvailable( true ) , m_VtkWrappingAvailable( true ) , m_ErrorOccured( false ) { bool pythonInitialized = static_cast( Py_IsInitialized() ); //m_PythonManager.isPythonInitialized() ); // due to strange static var behaviour on windows Py_IsInitialized() returns correct value while // m_PythonManager.isPythonInitialized() does not because it has been constructed and destructed again if( !pythonInitialized ) { MITK_INFO << "Initializing python service"; //TODO a better way to do this #ifndef WIN32 dlerror(); if(dlopen(PYTHON_LIBRARY, RTLD_NOW | RTLD_GLOBAL) == nullptr ) { mitkThrow() << "Python runtime could not be loaded: " << dlerror(); } #endif std::string programPath = QCoreApplication::applicationDirPath().toStdString() + "/"; QString pythonCommand; pythonCommand.append( QString("import site, sys\n") ); pythonCommand.append( QString("import SimpleITK as sitk\n") ); pythonCommand.append( QString("import SimpleITK._SimpleITK as _SimpleITK\n") ); pythonCommand.append( QString("import numpy\n") ); pythonCommand.append( QString("sys.path.append('')\n") ); pythonCommand.append( QString("sys.path.append('%1')\n").arg(programPath.c_str()) ); pythonCommand.append( QString("sys.path.append('%1')\n").arg(EXTERNAL_DIST_PACKAGES) ); pythonCommand.append( QString("\nsite.addsitedir('%1')").arg(EXTERNAL_SITE_PACKAGES) ); if( pythonInitialized ) m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut|PythonQt::PythonAlreadyInitialized); else m_PythonManager.setInitializationFlags(PythonQt::RedirectStdOut); m_PythonManager.initialize(); m_PythonManager.executeString( pythonCommand, ctkAbstractPythonManager::FileInput ); } } mitk::PythonService::~PythonService() { MITK_DEBUG("mitk::PythonService") << "destructing PythonService"; } void mitk::PythonService::AddRelativeSearchDirs(std::vector< std::string > dirs) { std::string programPath = QCoreApplication::applicationDirPath().toStdString() + "/"; std::string cwd = ist::GetCurrentWorkingDirectory() + "/"; for (auto dir : dirs) { m_PythonManager.executeString(QString("sys.path.append('%1')").arg((programPath + dir).c_str()), ctkAbstractPythonManager::SingleInput ); m_PythonManager.executeString(QString("sys.path.append('%1')").arg((cwd + dir).c_str()), ctkAbstractPythonManager::SingleInput ); } } void mitk::PythonService::AddAbsoluteSearchDirs(std::vector< std::string > dirs) { for (auto dir : dirs) { m_PythonManager.executeString(QString("sys.path.append('%1')").arg(dir.c_str()), ctkAbstractPythonManager::SingleInput ); } } std::string mitk::PythonService::Execute(const std::string &stdpythonCommand, int commandType) { QString pythonCommand = QString::fromStdString(stdpythonCommand); QVariant result; bool commandIssued = true; if(commandType == IPythonService::SINGLE_LINE_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::SingleInput ); else if(commandType == IPythonService::MULTI_LINE_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::FileInput ); else if(commandType == IPythonService::EVAL_COMMAND ) result = m_PythonManager.executeString(pythonCommand, ctkAbstractPythonManager::EvalInput ); else commandIssued = false; if(commandIssued) { this->NotifyObserver(pythonCommand.toStdString()); m_ErrorOccured = PythonQt::self()->hadError(); } return result.toString().toStdString(); } void mitk::PythonService::ExecuteScript( const std::string& pythonScript ) { std::ifstream t(pythonScript.c_str()); std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); t.close(); m_PythonManager.executeString(QString::fromStdString(str)); } std::vector mitk::PythonService::GetVariableStack() const { std::vector list; PyObject* dict = PyImport_GetModuleDict(); PyObject* object = PyDict_GetItemString(dict, "__main__"); PyObject* dirMain = PyObject_Dir(object); PyObject* tempObject = nullptr; //PyObject* strTempObject = 0; if(dirMain) { std::string name, attrValue, attrType; for(int i = 0; iob_type->tp_name; if(tempObject && ( PyUnicode_Check(tempObject) || PyString_Check(tempObject) ) ) attrValue = PyString_AsString(tempObject); else attrValue = ""; mitk::PythonVariable var; var.m_Name = name; var.m_Value = attrValue; var.m_Type = attrType; list.push_back(var); } } return list; } std::string mitk::PythonService::GetVariable(const std::string& name) const { std::vector allVars = this->GetVariableStack(); for(unsigned int i = 0; i< allVars.size(); i++) { if( allVars.at(i).m_Name == name ) return allVars.at(i).m_Value; } return ""; } bool mitk::PythonService::DoesVariableExist(const std::string& name) const { bool varExists = false; std::vector allVars = this->GetVariableStack(); for(unsigned int i = 0; i< allVars.size(); i++) { if( allVars.at(i).m_Name == name ) { varExists = true; break; } } return varExists; } void mitk::PythonService::AddPythonCommandObserver(mitk::PythonCommandObserver *observer) { if(!m_Observer.contains(observer)) m_Observer.append(observer); } void mitk::PythonService::RemovePythonCommandObserver(mitk::PythonCommandObserver *observer) { m_Observer.removeOne(observer); } void mitk::PythonService::NotifyObserver(const std::string &command) { MITK_DEBUG("mitk::PythonService") << "number of observer " << m_Observer.size(); for( int i=0; i< m_Observer.size(); ++i ) { m_Observer.at(i)->CommandExecuted(command); } } bool mitk::PythonService::CopyToPythonAsSimpleItkImage(mitk::Image *image, const std::string &stdvarName) { QString varName = QString::fromStdString( stdvarName ); QString command; unsigned int* imgDim = image->GetDimensions(); int npy_nd = 1; // access python module PyObject *pyMod = PyImport_AddModule("__main__"); // global dictionary PyObject *pyDict = PyModule_GetDict(pyMod); const mitk::Vector3D spacing = image->GetGeometry()->GetSpacing(); const mitk::Point3D origin = image->GetGeometry()->GetOrigin(); mitk::PixelType pixelType = image->GetPixelType(); itk::ImageIOBase::IOPixelType ioPixelType = image->GetPixelType().GetPixelType(); PyObject* npyArray = nullptr; mitk::ImageReadAccessor racc(image); void* array = const_cast(racc.GetData()); mitk::Vector3D xDirection; mitk::Vector3D yDirection; mitk::Vector3D zDirection; const vnl_matrix_fixed &transform = image->GetGeometry()->GetIndexToWorldTransform()->GetMatrix().GetVnlMatrix(); mitk::Vector3D s = image->GetGeometry()->GetSpacing(); // ToDo: Check if this is a collumn or row vector from the matrix. // right now it works but not sure for rotated geometries mitk::FillVector3D(xDirection, transform[0][0]/s[0], transform[0][1]/s[1], transform[0][2]/s[2]); mitk::FillVector3D(yDirection, transform[1][0]/s[0], transform[1][1]/s[1], transform[1][2]/s[2]); mitk::FillVector3D(zDirection, transform[2][0]/s[0], transform[2][1]/s[1], transform[2][2]/s[2]); // save the total number of elements here (since the numpy array is one dimensional) npy_intp* npy_dims = new npy_intp[1]; npy_dims[0] = imgDim[0]; /** * Build a string in the format [1024,1028,1] * to describe the dimensionality. This is needed for simple itk * to know the dimensions of the image */ QString dimensionString; dimensionString.append(QString("[")); dimensionString.append(QString::number(imgDim[0])); for (unsigned i = 1; i < 3; ++i) // always three because otherwise the 3d-geometry gets destroyed // (relevant for backtransformation of simple itk image to mitk. { dimensionString.append(QString(",")); dimensionString.append(QString::number(imgDim[i])); npy_dims[0] *= imgDim[i]; } dimensionString.append("]"); // the next line is necessary for vectorimages npy_dims[0] *= pixelType.GetNumberOfComponents(); // default pixeltype: unsigned short NPY_TYPES npy_type = NPY_USHORT; std::string sitk_type = "sitkUInt8"; if( ioPixelType == itk::ImageIOBase::SCALAR ) { if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) { npy_type = NPY_DOUBLE; sitk_type = "sitkFloat64"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) { npy_type = NPY_FLOAT; sitk_type = "sitkFloat32"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) { npy_type = NPY_SHORT; sitk_type = "sitkInt16"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) { npy_type = NPY_BYTE; sitk_type = "sitkInt8"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) { npy_type = NPY_INT; sitk_type = "sitkInt32"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) { npy_type = NPY_LONG; sitk_type = "sitkInt64"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) { npy_type = NPY_UBYTE; sitk_type = "sitkUInt8"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) { npy_type = NPY_UINT; sitk_type = "sitkUInt32"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) { npy_type = NPY_LONG; sitk_type = "sitkUInt64"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) { npy_type = NPY_USHORT; sitk_type = "sitkUInt16"; } } else if ( ioPixelType == itk::ImageIOBase::VECTOR || ioPixelType == itk::ImageIOBase::RGB || ioPixelType == itk::ImageIOBase::RGBA ) { if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) { npy_type = NPY_DOUBLE; sitk_type = "sitkVectorFloat64"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) { npy_type = NPY_FLOAT; sitk_type = "sitkVectorFloat32"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) { npy_type = NPY_SHORT; sitk_type = "sitkVectorInt16"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) { npy_type = NPY_BYTE; sitk_type = "sitkVectorInt8"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) { npy_type = NPY_INT; sitk_type = "sitkVectorInt32"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) { npy_type = NPY_LONG; sitk_type = "sitkVectorInt64"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) { npy_type = NPY_UBYTE; sitk_type = "sitkVectorUInt8"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) { npy_type = NPY_UINT; sitk_type = "sitkVectorUInt32"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) { npy_type = NPY_LONG; sitk_type = "sitkVectorUInt64"; } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) { npy_type = NPY_USHORT; sitk_type = "sitkVectorUInt16"; } } else { MITK_WARN << "not a recognized pixeltype"; return false; } // creating numpy array import_array1 (true); npyArray = PyArray_SimpleNewFromData(npy_nd,npy_dims,npy_type,array); // add temp array it to the python dictionary to access it in python code const int status = PyDict_SetItemString( pyDict,QString("%1_numpy_array") .arg(varName).toStdString().c_str(), npyArray ); // sanity check if ( status != 0 ) return false; command.append( QString("%1 = sitk.Image(%2,sitk.%3,%4)\n").arg(varName) .arg(dimensionString) .arg(QString(sitk_type.c_str())).arg(QString::number(pixelType.GetNumberOfComponents())) ); command.append( QString("%1.SetSpacing([%2,%3,%4])\n").arg(varName) .arg(QString::number(spacing[0])) .arg(QString::number(spacing[1])) .arg(QString::number(spacing[2])) ); command.append( QString("%1.SetOrigin([%2,%3,%4])\n").arg(varName) .arg(QString::number(origin[0])) .arg(QString::number(origin[1])) .arg(QString::number(origin[2])) ); command.append( QString("%1.SetDirection([%2,%3,%4,%5,%6,%7,%8,%9,%10])\n").arg(varName) .arg(QString::number(xDirection[0])) .arg(QString::number(xDirection[1])) .arg(QString::number(xDirection[2])) .arg(QString::number(yDirection[0])) .arg(QString::number(yDirection[1])) .arg(QString::number(yDirection[2])) .arg(QString::number(zDirection[0])) .arg(QString::number(zDirection[1])) .arg(QString::number(zDirection[2])) ); // directly access the cpp api from the lib command.append( QString("_SimpleITK._SetImageFromArray(%1_numpy_array,%1)\n").arg(varName) ); command.append( QString("del %1_numpy_array").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute( command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return true; } mitk::PixelType DeterminePixelType(const std::string& pythonPixeltype, unsigned long nrComponents, int dimensions) { typedef itk::RGBPixel< unsigned char > UCRGBPixelType; typedef itk::RGBPixel< unsigned short > USRGBPixelType; typedef itk::RGBPixel< float > FloatRGBPixelType; typedef itk::RGBPixel< double > DoubleRGBPixelType; typedef itk::Image< UCRGBPixelType > UCRGBImageType; typedef itk::Image< USRGBPixelType > USRGBImageType; typedef itk::Image< FloatRGBPixelType > FloatRGBImageType; typedef itk::Image< DoubleRGBPixelType > DoubleRGBImageType; typedef itk::RGBAPixel< unsigned char > UCRGBAPixelType; typedef itk::RGBAPixel< unsigned short > USRGBAPixelType; typedef itk::RGBAPixel< float > FloatRGBAPixelType; typedef itk::RGBAPixel< double > DoubleRGBAPixelType; typedef itk::Image< UCRGBAPixelType > UCRGBAImageType; typedef itk::Image< USRGBAPixelType > USRGBAImageType; typedef itk::Image< FloatRGBAPixelType > FloatRGBAImageType; typedef itk::Image< DoubleRGBAPixelType > DoubleRGBAImageType; auto pixelType = mitk::MakePixelType(nrComponents); if (nrComponents == 1) { if( pythonPixeltype.compare("float64") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("float32") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("int16") == 0) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("int8") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("int32") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("int64") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("uint8") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("uint32") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("uint64") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else if( pythonPixeltype.compare("uint16") == 0 ) { pixelType = mitk::MakePixelType(nrComponents); } else { mitkThrow()<< "unknown scalar PixelType"; } } else if(nrComponents == 3 && dimensions == 2) { if( pythonPixeltype.compare("float64") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("float32") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("uint8") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("uint16") == 0 ) { pixelType = mitk::MakePixelType(); } } else if( (nrComponents == 4) && dimensions == 2 ) { if( pythonPixeltype.compare("float64") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("float32") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("uint8") == 0 ) { pixelType = mitk::MakePixelType(); } else if( pythonPixeltype.compare("uint16") == 0 ) { pixelType = mitk::MakePixelType(); } } else { if( pythonPixeltype.compare("float64") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("float32") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("int16") == 0) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("int8") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("int32") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("int64") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("uint8") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("uint16") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("uint32") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else if( pythonPixeltype.compare("uint64") == 0 ) { pixelType = mitk::MakePixelType >(nrComponents); } else { mitkThrow()<< "unknown vectorial PixelType"; } } return pixelType; } mitk::Image::Pointer mitk::PythonService::CopySimpleItkImageFromPython(const std::string &stdvarName) { double*ds = nullptr; // access python module PyObject *pyMod = PyImport_AddModule("__main__"); // global dictionarry PyObject *pyDict = PyModule_GetDict(pyMod); mitk::Image::Pointer mitkImage = mitk::Image::New(); mitk::Vector3D spacing; mitk::Point3D origin; QString command; QString varName = QString::fromStdString( stdvarName ); command.append( QString("%1_numpy_array = sitk.GetArrayFromImage(%1)\n").arg(varName) ); command.append( QString("%1_spacing = numpy.asarray(%1.GetSpacing())\n").arg(varName) ); command.append( QString("%1_origin = numpy.asarray(%1.GetOrigin())\n").arg(varName) ); command.append( QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName) ); command.append( QString("%1_direction = numpy.asarray(%1.GetDirection())\n").arg(varName) ); command.append( QString("%1_nrComponents = numpy.asarray(%1.GetNumberOfComponentsPerPixel())\n").arg(varName)); command.append( QString("%1_dtype = %1_numpy_array.dtype.name\n").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); PyObject* py_dtype = PyDict_GetItemString(pyDict,QString("%1_dtype").arg(varName).toStdString().c_str() ); std::string dtype = PyString_AsString(py_dtype); PyArrayObject* py_data = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_numpy_array").arg(varName).toStdString().c_str() ); PyArrayObject* py_spacing = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_spacing").arg(varName).toStdString().c_str() ); PyArrayObject* py_origin = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_origin").arg(varName).toStdString().c_str() ); PyArrayObject* py_direction = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_direction").arg(varName).toStdString().c_str() ); PyArrayObject* py_nrComponents = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_nrComponents").arg(varName).toStdString().c_str() ); unsigned int nr_Components = *(reinterpret_cast(PyArray_DATA(py_nrComponents))); unsigned int nr_dimensions = PyArray_NDIM(py_data); if (nr_Components > 1) // for VectorImages the last dimension in the numpy array are the vector components. { --nr_dimensions; } mitk::PixelType pixelType = DeterminePixelType(dtype, nr_Components, nr_dimensions); unsigned int* dimensions = new unsigned int[nr_dimensions]; // fill backwards , nd data saves dimensions in opposite direction for( unsigned i = 0; i < nr_dimensions; ++i ) { dimensions[i] = PyArray_DIMS(py_data)[nr_dimensions - 1 - i]; } mitkImage->Initialize(pixelType, nr_dimensions, dimensions); mitkImage->SetChannel(PyArray_DATA(py_data)); ds = reinterpret_cast(PyArray_DATA(py_spacing)); spacing[0] = ds[0]; spacing[1] = ds[1]; spacing[2] = ds[2]; mitkImage->GetGeometry()->SetSpacing(spacing); ds = reinterpret_cast(PyArray_DATA(py_origin)); origin[0] = ds[0]; origin[1] = ds[1]; origin[2] = ds[2]; mitkImage->GetGeometry()->SetOrigin(origin); itk::Matrix py_transform; ds = reinterpret_cast(PyArray_DATA(py_direction)); py_transform[0][0] = ds[0]; py_transform[0][1] = ds[1]; py_transform[0][2] = ds[2]; py_transform[1][0] = ds[3]; py_transform[1][1] = ds[4]; py_transform[1][2] = ds[5]; py_transform[2][0] = ds[6]; py_transform[2][1] = ds[7]; py_transform[2][2] = ds[8]; mitk::AffineTransform3D::Pointer affineTransform = mitkImage->GetGeometry()->GetIndexToWorldTransform(); itk::Matrix transform = py_transform * affineTransform->GetMatrix(); affineTransform->SetMatrix(transform); mitkImage->GetGeometry()->SetIndexToWorldTransform(affineTransform); // mitk::AffineTransform3D::New(); //mitkImage->GetGeometry()->SetIndexToWorldTransform(); // cleanup command.clear(); command.append( QString("del %1_numpy_array\n").arg(varName) ); command.append( QString("del %1_dtype\n").arg(varName) ); command.append( QString("del %1_spacing\n").arg(varName) ); command.append( QString("del %1_origin\n").arg(varName) ); command.append( QString("del %1_direction\n").arg(varName) ); command.append( QString("del %1_nrComponents\n").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); delete[] dimensions; return mitkImage; } bool mitk::PythonService::CopyToPythonAsCvImage( mitk::Image* image, const std::string& stdvarName ) { QString varName = QString::fromStdString( stdvarName ); QString command; unsigned int* imgDim = image->GetDimensions(); int npy_nd = 1; // access python module PyObject *pyMod = PyImport_AddModule((char*)"__main__"); // global dictionary PyObject *pyDict = PyModule_GetDict(pyMod); mitk::PixelType pixelType = image->GetPixelType(); PyObject* npyArray = nullptr; mitk::ImageReadAccessor racc(image); void* array = (void*) racc.GetData(); // save the total number of elements here (since the numpy array is one dimensional) npy_intp* npy_dims = new npy_intp[1]; npy_dims[0] = imgDim[0]; /** * Build a string in the format [1024,1028,1] * to describe the dimensionality. This is needed for simple itk * to know the dimensions of the image */ QString dimensionString; dimensionString.append(QString("[")); dimensionString.append(QString::number(imgDim[0])); // ToDo: check if we need this for (unsigned i = 1; i < 3; ++i) // always three because otherwise the 3d-geometry gets destroyed // (relevant for backtransformation of simple itk image to mitk. { dimensionString.append(QString(",")); dimensionString.append(QString::number(imgDim[i])); npy_dims[0] *= imgDim[i]; } dimensionString.append("]"); // the next line is necessary for vectorimages npy_dims[0] *= pixelType.GetNumberOfComponents(); // default pixeltype: unsigned short NPY_TYPES npy_type = NPY_USHORT; if( pixelType.GetComponentType() == itk::ImageIOBase::DOUBLE ) { npy_type = NPY_DOUBLE; } else if( pixelType.GetComponentType() == itk::ImageIOBase::FLOAT ) { npy_type = NPY_FLOAT; } else if( pixelType.GetComponentType() == itk::ImageIOBase::SHORT) { npy_type = NPY_SHORT; } else if( pixelType.GetComponentType() == itk::ImageIOBase::CHAR ) { npy_type = NPY_BYTE; } else if( pixelType.GetComponentType() == itk::ImageIOBase::INT ) { npy_type = NPY_INT; } else if( pixelType.GetComponentType() == itk::ImageIOBase::LONG ) { npy_type = NPY_LONG; } else if( pixelType.GetComponentType() == itk::ImageIOBase::UCHAR ) { npy_type = NPY_UBYTE; } else if( pixelType.GetComponentType() == itk::ImageIOBase::UINT ) { npy_type = NPY_UINT; } else if( pixelType.GetComponentType() == itk::ImageIOBase::ULONG ) { npy_type = NPY_LONG; } else if( pixelType.GetComponentType() == itk::ImageIOBase::USHORT ) { npy_type = NPY_USHORT; } else { MITK_WARN << "not a recognized pixeltype"; return false; } // creating numpy array import_array1 (true); npyArray = PyArray_SimpleNewFromData(npy_nd,npy_dims,npy_type,array); // add temp array it to the python dictionary to access it in python code const int status = PyDict_SetItemString( pyDict,QString("%1_numpy_array") .arg(varName).toStdString().c_str(), npyArray ); // sanity check if ( status != 0 ) return false; command.append( QString("import numpy as np\n")); //command.append( QString("if '%1' in globals():\n").arg(varName)); //command.append( QString(" del %1\n").arg(varName)); command.append( QString("%1_array_tmp=%1_numpy_array.copy()\n").arg(varName)); command.append( QString("%1_array_tmp=%1_array_tmp.reshape(%2,%3,%4)\n").arg( varName, QString::number(imgDim[1]), QString::number(imgDim[0]), QString::number(pixelType.GetNumberOfComponents()))); command.append( QString("%1 = %1_array_tmp[:,...,::-1]\n").arg(varName)); command.append( QString("del %1_numpy_array\n").arg(varName) ); command.append( QString("del %1_array_tmp").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute( command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return true; } mitk::Image::Pointer mitk::PythonService::CopyCvImageFromPython( const std::string& stdvarName ) { // access python module PyObject *pyMod = PyImport_AddModule((char*)"__main__"); // global dictionarry PyObject *pyDict = PyModule_GetDict(pyMod); mitk::Image::Pointer mitkImage = mitk::Image::New(); QString command; QString varName = QString::fromStdString( stdvarName ); command.append( QString("import numpy as np\n")); command.append( QString("%1_dtype=%1.dtype.name\n").arg(varName) ); command.append( QString("%1_shape=np.asarray(%1.shape)\n").arg(varName) ); command.append( QString("%1_np_array=%1[:,...,::-1]\n").arg(varName)); command.append( QString("%1_np_array=np.reshape(%1_np_array,%1.shape[0] * %1.shape[1] * %1.shape[2])").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); PyObject* py_dtype = PyDict_GetItemString(pyDict,QString("%1_dtype").arg(varName).toStdString().c_str() ); std::string dtype = PyString_AsString(py_dtype); PyArrayObject* py_data = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_np_array").arg(varName).toStdString().c_str() ); PyArrayObject* shape = (PyArrayObject*) PyDict_GetItemString(pyDict,QString("%1_shape").arg(varName).toStdString().c_str() ); size_t* d = reinterpret_cast(PyArray_DATA(shape)); unsigned int dimensions[3]; dimensions[0] = d[1]; dimensions[1] = d[0]; dimensions[2] = d[2]; unsigned int nr_dimensions = 2; // get number of components unsigned int nr_Components = (unsigned int) d[2]; auto pixelType = DeterminePixelType(dtype, nr_Components, nr_dimensions); mitkImage->Initialize(pixelType, nr_dimensions, dimensions); //mitkImage->SetChannel(py_data->data); { mitk::ImageWriteAccessor ra(mitkImage); char* data = (char*)(ra.GetData()); memcpy(data, PyArray_DATA(py_data), dimensions[0] * dimensions[1] * pixelType.GetSize()); } command.clear(); command.append( QString("del %1_shape\n").arg(varName) ); command.append( QString("del %1_dtype\n").arg(varName) ); command.append( QString("del %1_np_array").arg(varName)); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return mitkImage; } ctkAbstractPythonManager *mitk::PythonService::GetPythonManager() { return &m_PythonManager; } mitk::Surface::Pointer mitk::PythonService::CopyVtkPolyDataFromPython( const std::string& stdvarName ) { // access python module PyObject *pyMod = PyImport_AddModule((char*)"__main__"); // global dictionarry PyObject *pyDict = PyModule_GetDict(pyMod); // python memory address PyObject *pyAddr = nullptr; // cpp address size_t addr = 0; mitk::Surface::Pointer surface = mitk::Surface::New(); QString command; QString varName = QString::fromStdString( stdvarName ); command.append( QString("%1_addr_str = %1.GetAddressAsString(\"vtkPolyData\")\n").arg(varName) ); // remove 0x from the address command.append( QString("%1_addr = int(%1_addr_str[5:],16)").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); // get address of the object pyAddr = PyDict_GetItemString(pyDict,QString("%1_addr").arg(varName).toStdString().c_str()); // convert to long addr = PyInt_AsLong(pyAddr); MITK_DEBUG << "Python object address: " << addr; // get the object vtkPolyData* poly = (vtkPolyData*)((void*)addr); surface->SetVtkPolyData(poly); // delete helper variables from python stack command = ""; command.append( QString("del %1_addr_str\n").arg(varName) ); command.append( QString("del %1_addr").arg(varName) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return surface; } bool mitk::PythonService::CopyToPythonAsVtkPolyData( mitk::Surface* surface, const std::string& stdvarName ) { QString varName = QString::fromStdString( stdvarName ); std::ostringstream oss; std::string addr = ""; QString command; QString address; oss << (void*) ( surface->GetVtkPolyData() ); // get the address addr = oss.str(); // remove "0x" address = QString::fromStdString(addr.substr(2)); command.append( QString("%1 = vtk.vtkPolyData(\"%2\")\n").arg(varName).arg(address) ); MITK_DEBUG("PythonService") << "Issuing python command " << command.toStdString(); this->Execute(command.toStdString(), IPythonService::MULTI_LINE_COMMAND ); return true; } bool mitk::PythonService::IsSimpleItkPythonWrappingAvailable() { this->Execute( "import SimpleITK as sitk\n", IPythonService::SINGLE_LINE_COMMAND ); // directly access cpp lib this->Execute( "import SimpleITK._SimpleITK as _SimpleITK\n", IPythonService::SINGLE_LINE_COMMAND ); m_ItkWrappingAvailable = !this->PythonErrorOccured(); // check for numpy this->Execute( "import numpy\n", IPythonService::SINGLE_LINE_COMMAND ); if ( this->PythonErrorOccured() ) MITK_ERROR << "Numpy not found."; m_ItkWrappingAvailable = !this->PythonErrorOccured(); return m_ItkWrappingAvailable; } bool mitk::PythonService::IsOpenCvPythonWrappingAvailable() { this->Execute( "import cv2\n", IPythonService::SINGLE_LINE_COMMAND ); m_OpenCVWrappingAvailable = !this->PythonErrorOccured(); return m_OpenCVWrappingAvailable; } bool mitk::PythonService::IsVtkPythonWrappingAvailable() { this->Execute( "import vtk", IPythonService::SINGLE_LINE_COMMAND ); //this->Execute( "print \"Using VTK version \" + vtk.vtkVersion.GetVTKVersion()\n", IPythonService::SINGLE_LINE_COMMAND ); m_VtkWrappingAvailable = !this->PythonErrorOccured(); return m_VtkWrappingAvailable; } bool mitk::PythonService::PythonErrorOccured() const { return m_ErrorOccured; } diff --git a/Modules/QtWidgets/src/QmitkAbstractNodeSelectionWidget.cpp b/Modules/QtWidgets/src/QmitkAbstractNodeSelectionWidget.cpp index 7b77409216..ff1ca235c1 100644 --- a/Modules/QtWidgets/src/QmitkAbstractNodeSelectionWidget.cpp +++ b/Modules/QtWidgets/src/QmitkAbstractNodeSelectionWidget.cpp @@ -1,416 +1,419 @@ /*============================================================================ 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 "QmitkAbstractNodeSelectionWidget.h" #include "QmitkModelViewSelectionConnector.h" QmitkAbstractNodeSelectionWidget::QmitkAbstractNodeSelectionWidget(QWidget* parent) : QWidget(parent), m_InvalidInfo("Error. Select data."), m_EmptyInfo("Empty. Make a selection."), m_PopUpTitel("Select a data node"), m_PopUpHint(""), m_IsOptional(false), m_SelectOnlyVisibleNodes(true), m_DataStorageDeletedTag(0), m_LastEmissionAllowance(true), m_RecursionGuard(false) { } QmitkAbstractNodeSelectionWidget::~QmitkAbstractNodeSelectionWidget() { auto dataStorage = m_DataStorage.Lock(); if (dataStorage.IsNotNull()) { // remove Listener for the data storage itself dataStorage->RemoveObserver(m_DataStorageDeletedTag); // remove "add node listener" from data storage dataStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeAddedToStorage)); // remove "remove node listener" from data storage dataStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage)); } for (auto& node : m_CurrentInternalSelection) { this->RemoveNodeObserver(node); } } QmitkAbstractNodeSelectionWidget::NodeList QmitkAbstractNodeSelectionWidget::GetSelectedNodes() const { return this->CompileEmitSelection(); } QmitkAbstractNodeSelectionWidget::ConstNodeStdVector QmitkAbstractNodeSelectionWidget::GetSelectedNodesStdVector() const { auto result = this->GetSelectedNodes(); return ConstNodeStdVector(result.begin(), result.end()); } void QmitkAbstractNodeSelectionWidget::SetDataStorage(mitk::DataStorage* dataStorage) { if (m_DataStorage == dataStorage) { return; } auto oldStorage = m_DataStorage.Lock(); if (oldStorage.IsNotNull()) { // remove Listener for the data storage itself oldStorage->RemoveObserver(m_DataStorageDeletedTag); // remove "add node listener" from old data storage oldStorage->AddNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeAddedToStorage)); // remove "remove node listener" from old data storage oldStorage->RemoveNodeEvent.RemoveListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage)); } m_DataStorage = dataStorage; auto newStorage = m_DataStorage.Lock(); if (newStorage.IsNotNull()) { // add Listener for the data storage itself auto command = itk::SimpleMemberCommand::New(); command->SetCallbackFunction(this, &QmitkAbstractNodeSelectionWidget::SetDataStorageDeleted); m_DataStorageDeletedTag = newStorage->AddObserver(itk::DeleteEvent(), command); // add "add node listener" for new data storage newStorage->AddNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeAddedToStorage)); // add remove node listener for new data storage newStorage->RemoveNodeEvent.AddListener( mitk::MessageDelegate1(this, &QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage)); } this->OnDataStorageChanged(); this->HandleChangeOfInternalSelection({}); } void QmitkAbstractNodeSelectionWidget::SetNodePredicate(const mitk::NodePredicateBase* nodePredicate) { if (m_NodePredicate != nodePredicate) { m_NodePredicate = nodePredicate; this->OnNodePredicateChanged(); NodeList newInternalNodes; for (auto& node : m_CurrentInternalSelection) { if (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node)) { newInternalNodes.append(node); } } if (!m_SelectOnlyVisibleNodes) { for (auto& node : m_CurrentExternalSelection) { if (!newInternalNodes.contains(node) && (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node))) { newInternalNodes.append(node); } } } this->HandleChangeOfInternalSelection(newInternalNodes); } } void QmitkAbstractNodeSelectionWidget::HandleChangeOfInternalSelection(NodeList newInternalSelection) { - this->ReviseSelectionChanged(m_CurrentInternalSelection, newInternalSelection); + if (!EqualNodeSelections(m_CurrentInternalSelection, newInternalSelection)) + { + this->ReviseSelectionChanged(m_CurrentInternalSelection, newInternalSelection); - this->SetCurrentInternalSelection(newInternalSelection); + this->SetCurrentInternalSelection(newInternalSelection); - this->OnInternalSelectionChanged(); + this->OnInternalSelectionChanged(); - auto newEmission = this->CompileEmitSelection(); + auto newEmission = this->CompileEmitSelection(); - this->EmitSelection(newEmission); + this->EmitSelection(newEmission); - this->UpdateInfo(); + this->UpdateInfo(); + } } void QmitkAbstractNodeSelectionWidget::SetCurrentSelection(NodeList selectedNodes) { if (!m_RecursionGuard) { m_CurrentExternalSelection = selectedNodes; auto dataStorage = m_DataStorage.Lock(); NodeList newInternalSelection; for (auto node : selectedNodes) { if (dataStorage.IsNotNull() && dataStorage->Exists(node) && (m_NodePredicate.IsNull() || m_NodePredicate->CheckNode(node))) { newInternalSelection.append(node); } } this->HandleChangeOfInternalSelection(newInternalSelection); } } const mitk::NodePredicateBase* QmitkAbstractNodeSelectionWidget::GetNodePredicate() const { return m_NodePredicate; } QString QmitkAbstractNodeSelectionWidget::GetInvalidInfo() const { return m_InvalidInfo; } QString QmitkAbstractNodeSelectionWidget::GetEmptyInfo() const { return m_EmptyInfo; } QString QmitkAbstractNodeSelectionWidget::GetPopUpTitel() const { return m_PopUpTitel; } QString QmitkAbstractNodeSelectionWidget::GetPopUpHint() const { return m_PopUpHint; } bool QmitkAbstractNodeSelectionWidget::GetSelectionIsOptional() const { return m_IsOptional; } bool QmitkAbstractNodeSelectionWidget::GetSelectOnlyVisibleNodes() const { return m_SelectOnlyVisibleNodes; } void QmitkAbstractNodeSelectionWidget::SetSelectOnlyVisibleNodes(bool selectOnlyVisibleNodes) { if (m_SelectOnlyVisibleNodes != selectOnlyVisibleNodes) { m_SelectOnlyVisibleNodes = selectOnlyVisibleNodes; auto newEmission = this->CompileEmitSelection(); this->EmitSelection(newEmission); } } void QmitkAbstractNodeSelectionWidget::SetInvalidInfo(QString info) { m_InvalidInfo = info; this->UpdateInfo(); } void QmitkAbstractNodeSelectionWidget::SetEmptyInfo(QString info) { m_EmptyInfo = info; this->UpdateInfo(); } void QmitkAbstractNodeSelectionWidget::SetPopUpTitel(QString info) { m_PopUpTitel = info; } void QmitkAbstractNodeSelectionWidget::SetPopUpHint(QString info) { m_PopUpHint = info; } void QmitkAbstractNodeSelectionWidget::SetSelectionIsOptional(bool isOptional) { m_IsOptional = isOptional; this->UpdateInfo(); } void QmitkAbstractNodeSelectionWidget::SetDataStorageDeleted() { this->OnDataStorageChanged(); this->HandleChangeOfInternalSelection({}); } void QmitkAbstractNodeSelectionWidget::ReviseSelectionChanged(const NodeList& /*oldInternalSelection*/, NodeList& /*newInternalSelection*/) { } bool QmitkAbstractNodeSelectionWidget::AllowEmissionOfSelection(const NodeList& /*emissionCandidates*/) const { return true; } void QmitkAbstractNodeSelectionWidget::EmitSelection(const NodeList& emissionCandidates) { m_LastEmissionAllowance = this->AllowEmissionOfSelection(emissionCandidates); if (m_LastEmissionAllowance && !EqualNodeSelections(m_LastEmission, emissionCandidates)) { m_RecursionGuard = true; emit CurrentSelectionChanged(emissionCandidates); m_RecursionGuard = false; m_LastEmission = emissionCandidates; } } void QmitkAbstractNodeSelectionWidget::SetCurrentInternalSelection(NodeList selectedNodes) { for (auto& node : m_CurrentInternalSelection) { this->RemoveNodeObserver(node); } m_CurrentInternalSelection = selectedNodes; for (auto& node : m_CurrentInternalSelection) { this->AddNodeObserver(node); } } const QmitkAbstractNodeSelectionWidget::NodeList& QmitkAbstractNodeSelectionWidget::GetCurrentInternalSelection() const { return m_CurrentInternalSelection; } const QmitkAbstractNodeSelectionWidget::NodeList& QmitkAbstractNodeSelectionWidget::GetCurrentExternalSelection() const { return m_CurrentExternalSelection; } void QmitkAbstractNodeSelectionWidget::OnNodePredicateChanged() { } void QmitkAbstractNodeSelectionWidget::OnDataStorageChanged() { } void QmitkAbstractNodeSelectionWidget::OnInternalSelectionChanged() { } void QmitkAbstractNodeSelectionWidget::NodeAddedToStorage(const mitk::DataNode* node) { this->OnNodeAddedToStorage(node); } void QmitkAbstractNodeSelectionWidget::OnNodeAddedToStorage(const mitk::DataNode* /*node*/) { } void QmitkAbstractNodeSelectionWidget::NodeRemovedFromStorage(const mitk::DataNode* node) { this->OnNodeRemovedFromStorage(node); this->RemoveNodeFromSelection(node); } void QmitkAbstractNodeSelectionWidget::OnNodeRemovedFromStorage(const mitk::DataNode* /*node*/) { } QmitkAbstractNodeSelectionWidget::NodeList QmitkAbstractNodeSelectionWidget::CompileEmitSelection() const { NodeList result = m_CurrentInternalSelection; if (!m_SelectOnlyVisibleNodes) { for (auto node : m_CurrentExternalSelection) { if (!result.contains(node) && m_NodePredicate.IsNotNull() && !m_NodePredicate->CheckNode(node)) { result.append(node); } } } return result; } void QmitkAbstractNodeSelectionWidget::RemoveNodeFromSelection(const mitk::DataNode* node) { auto newSelection = m_CurrentInternalSelection; auto finding = std::find(std::begin(newSelection), std::end(newSelection), node); if (finding != std::end(newSelection)) { newSelection.erase(finding); this->HandleChangeOfInternalSelection(newSelection); } } void QmitkAbstractNodeSelectionWidget::OnNodeModified(const itk::Object * caller, const itk::EventObject & event) { if (itk::ModifiedEvent().CheckEvent(&event)) { auto node = dynamic_cast(caller); if (node) { if (m_NodePredicate.IsNotNull() && !m_NodePredicate->CheckNode(node)) { this->RemoveNodeFromSelection(node); } else { auto oldAllowance = m_LastEmissionAllowance; auto newEmission = this->CompileEmitSelection(); auto nonConstNode = const_cast(node); if (newEmission.contains(nonConstNode) && (oldAllowance != this->AllowEmissionOfSelection(newEmission))) { this->EmitSelection(newEmission); this->UpdateInfo(); } } } } } void QmitkAbstractNodeSelectionWidget::AddNodeObserver(mitk::DataNode* node) { if (node) { auto modifiedCommand = itk::MemberCommand::New(); modifiedCommand->SetCallbackFunction(this, &QmitkAbstractNodeSelectionWidget::OnNodeModified); auto nodeModifiedObserverTag = node->AddObserver(itk::ModifiedEvent(), modifiedCommand); m_NodeObserverTags.insert(std::make_pair(node, nodeModifiedObserverTag)); } } void QmitkAbstractNodeSelectionWidget::RemoveNodeObserver(mitk::DataNode* node) { if (node) { auto finding = m_NodeObserverTags.find(node); if (finding != std::end(m_NodeObserverTags)) { node->RemoveObserver(finding->second); } else { MITK_ERROR << "Selection widget is in a wrong state. A node should be removed from the internal selection but seems to have no observer. Node:" << node; } m_NodeObserverTags.erase(node); } } diff --git a/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp b/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp index 0b85e1b296..bf9590c68f 100644 --- a/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp +++ b/Modules/QtWidgets/test/QmitkAbstractNodeSelectionWidgetTest.cpp @@ -1,619 +1,619 @@ /*============================================================================ 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 "QmitkAbstractNodeSelectionWidget.h" #include #include #include #include "QmitkModelViewSelectionConnector.h" #include #include #include extern std::vector globalCmdLineArgs; class TestWidget : public QmitkAbstractNodeSelectionWidget { public: explicit TestWidget(QWidget* parent = nullptr) : QmitkAbstractNodeSelectionWidget(parent), m_UpdateInfo(0), m_OnNodePredicateChanged(0), m_OnDataStorageChanged(0), m_OnInternalSelectionChanged(0), m_OnNodeAddedToStorage(0), m_OnNodeRemovedFromStorage(0), m_ReviseSelectionChanged(0), m_AllowEmissionOfSelection(0), m_Allow(true), m_NewSelectionEmited(0) { connect(this, &QmitkAbstractNodeSelectionWidget::CurrentSelectionChanged, this, &TestWidget::NewSelectionEmited); }; int m_UpdateInfo; void UpdateInfo() override { m_UpdateInfo++; }; int m_OnNodePredicateChanged; void OnNodePredicateChanged() override { m_OnNodePredicateChanged++; }; int m_OnDataStorageChanged; void OnDataStorageChanged() override { m_OnDataStorageChanged++; }; int m_OnInternalSelectionChanged; void OnInternalSelectionChanged() override { m_OnInternalSelectionChanged++; }; int m_OnNodeAddedToStorage; void OnNodeAddedToStorage(const mitk::DataNode* /*node*/) override { m_OnNodeAddedToStorage++; }; int m_OnNodeRemovedFromStorage; void OnNodeRemovedFromStorage(const mitk::DataNode* /*node*/) override { m_OnNodeRemovedFromStorage++; }; int m_ReviseSelectionChanged; void ReviseSelectionChanged(const NodeList& /*oldInternalSelection*/, NodeList& /*newInternalSelection*/) override { m_ReviseSelectionChanged++; }; mutable int m_AllowEmissionOfSelection; bool m_Allow; bool AllowEmissionOfSelection(const NodeList& emissionCandidates) const override { m_AllowEmissionOfSelection++; if (m_Allow) return QmitkAbstractNodeSelectionWidget::AllowEmissionOfSelection(emissionCandidates); return false; }; int m_NewSelectionEmited; QmitkAbstractNodeSelectionWidget::NodeList m_LastNewEmision; void NewSelectionEmited(NodeList selection) { m_NewSelectionEmited++; m_LastNewEmision = selection; }; }; class QmitkAbstractNodeSelectionWidgetTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(QmitkAbstractNodeSelectionWidgetTestSuite); MITK_TEST(SetDataStorageTest); MITK_TEST(DataStorageEventTest); MITK_TEST(NodePredicateTest); MITK_TEST(SelectOnlyVisibleNodesTest); MITK_TEST(OtherSetterAndGetterTest); MITK_TEST(AllowEmissionOfSelectionTest); MITK_TEST(OnNodeModifiedTest); MITK_TEST(SignalRecursionTest); CPPUNIT_TEST_SUITE_END(); mitk::DataStorage::Pointer m_DataStorage; mitk::DataNode::Pointer m_Node1; mitk::DataNode::Pointer m_Node1_2; mitk::DataNode::Pointer m_Node2; mitk::DataNode::Pointer m_Node3; QApplication* m_TestApp; public: void setUp() override { m_DataStorage = mitk::StandaloneDataStorage::New(); m_Node1 = mitk::DataNode::New(); m_Node1->SetName("node1_1"); m_Node2 = mitk::DataNode::New(); m_Node2->SetName("node2"); m_Node3 = mitk::DataNode::New(); m_Node3->SetName("node3"); m_Node1_2 = mitk::DataNode::New(); m_Node1_2->SetName("node1_2"); m_DataStorage->Add(m_Node1); m_DataStorage->Add(m_Node2); m_DataStorage->Add(m_Node3); m_DataStorage->Add(m_Node1_2); mitk::RenderingTestHelper::ArgcHelperClass cmdLineArgs(globalCmdLineArgs); auto argc = cmdLineArgs.GetArgc(); auto argv = cmdLineArgs.GetArgv(); m_TestApp = new QApplication(argc, argv); } mitk::NodePredicateBase::Pointer GeneratTestPredicate(const std::string& name) { auto check = [name](const mitk::DataNode * node) { return node->GetName().find(name,0) == 0; }; auto predicate = mitk::NodePredicateFunction::New(check); return predicate.GetPointer(); } void tearDown() override { delete m_TestApp; } void SetDataStorageTest() { TestWidget widget; widget.SetDataStorage(nullptr); CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged); CPPUNIT_ASSERT_EQUAL_MESSAGE("Set same data storage but triggered change", 0, widget.m_OnDataStorageChanged); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage); CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged); CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection); CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited); widget.SetDataStorage(m_DataStorage); - CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo); + CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged); - CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged); + CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage); - CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged); - CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection); + CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged); + CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection); CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited); widget.SetCurrentSelection({ m_Node1 }); - CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo); + CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged); - CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged); + CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage); - CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged); - CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection); + CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged); + CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection); CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited); CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.m_LastNewEmision)); CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes())); widget.SetDataStorage(m_DataStorage); - CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo); + CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged); CPPUNIT_ASSERT_EQUAL_MESSAGE("Set same data storage but triggered change", 1, widget.m_OnDataStorageChanged); - CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged); + CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage); - CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged); - CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection); + CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged); + CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection); CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited); CPPUNIT_ASSERT(EqualNodeSelections({ m_Node1 }, widget.GetSelectedNodes())); widget.SetDataStorage(nullptr); - CPPUNIT_ASSERT_EQUAL(3, widget.m_UpdateInfo); + CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged); CPPUNIT_ASSERT_EQUAL_MESSAGE("Set same data storage but triggered change", 2, widget.m_OnDataStorageChanged); - CPPUNIT_ASSERT_EQUAL(3, widget.m_OnInternalSelectionChanged); + CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeAddedToStorage); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage); - CPPUNIT_ASSERT_EQUAL(3, widget.m_ReviseSelectionChanged); - CPPUNIT_ASSERT_EQUAL(3, widget.m_AllowEmissionOfSelection); + CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged); + CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection); CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited); CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision)); CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes())); } void DataStorageEventTest() { TestWidget widget; auto newNode = mitk::DataNode::New(); widget.SetDataStorage(m_DataStorage); m_DataStorage->Add(newNode); - CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo); + CPPUNIT_ASSERT_EQUAL(0, widget.m_UpdateInfo); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged); - CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged); + CPPUNIT_ASSERT_EQUAL(0, widget.m_OnInternalSelectionChanged); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodeRemovedFromStorage); - CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged); - CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection); + CPPUNIT_ASSERT_EQUAL(0, widget.m_ReviseSelectionChanged); + CPPUNIT_ASSERT_EQUAL(0, widget.m_AllowEmissionOfSelection); CPPUNIT_ASSERT_EQUAL(0, widget.m_NewSelectionEmited); widget.SetCurrentSelection({ newNode }); m_DataStorage->Remove(m_Node1); - CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo); + CPPUNIT_ASSERT_EQUAL(1, widget.m_UpdateInfo); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged); - CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged); + CPPUNIT_ASSERT_EQUAL(1, widget.m_OnInternalSelectionChanged); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeRemovedFromStorage); - CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged); - CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection); + CPPUNIT_ASSERT_EQUAL(1, widget.m_ReviseSelectionChanged); + CPPUNIT_ASSERT_EQUAL(1, widget.m_AllowEmissionOfSelection); CPPUNIT_ASSERT_EQUAL(1, widget.m_NewSelectionEmited); CPPUNIT_ASSERT(EqualNodeSelections({ newNode }, widget.m_LastNewEmision)); CPPUNIT_ASSERT(EqualNodeSelections({ newNode }, widget.GetSelectedNodes())); m_DataStorage->Remove(newNode); - CPPUNIT_ASSERT_EQUAL(3, widget.m_UpdateInfo); + CPPUNIT_ASSERT_EQUAL(2, widget.m_UpdateInfo); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnDataStorageChanged); - CPPUNIT_ASSERT_EQUAL(3, widget.m_OnInternalSelectionChanged); + CPPUNIT_ASSERT_EQUAL(2, widget.m_OnInternalSelectionChanged); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage); CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodeRemovedFromStorage); - CPPUNIT_ASSERT_EQUAL(3, widget.m_ReviseSelectionChanged); - CPPUNIT_ASSERT_EQUAL(3, widget.m_AllowEmissionOfSelection); + CPPUNIT_ASSERT_EQUAL(2, widget.m_ReviseSelectionChanged); + CPPUNIT_ASSERT_EQUAL(2, widget.m_AllowEmissionOfSelection); CPPUNIT_ASSERT_EQUAL(2, widget.m_NewSelectionEmited); CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision)); CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes())); widget.SetCurrentSelection({ m_Node2 }); m_DataStorage = nullptr; - CPPUNIT_ASSERT_EQUAL(5, widget.m_UpdateInfo); + CPPUNIT_ASSERT_EQUAL(4, widget.m_UpdateInfo); CPPUNIT_ASSERT_EQUAL(0, widget.m_OnNodePredicateChanged); CPPUNIT_ASSERT_EQUAL(2, widget.m_OnDataStorageChanged); - CPPUNIT_ASSERT_EQUAL(5, widget.m_OnInternalSelectionChanged); + CPPUNIT_ASSERT_EQUAL(4, widget.m_OnInternalSelectionChanged); CPPUNIT_ASSERT_EQUAL(1, widget.m_OnNodeAddedToStorage); CPPUNIT_ASSERT_EQUAL(2, widget.m_OnNodeRemovedFromStorage); - CPPUNIT_ASSERT_EQUAL(5, widget.m_ReviseSelectionChanged); - CPPUNIT_ASSERT_EQUAL(5, widget.m_AllowEmissionOfSelection); + CPPUNIT_ASSERT_EQUAL(4, widget.m_ReviseSelectionChanged); + CPPUNIT_ASSERT_EQUAL(4, widget.m_AllowEmissionOfSelection); CPPUNIT_ASSERT_EQUAL(4, widget.m_NewSelectionEmited); CPPUNIT_ASSERT(EqualNodeSelections({}, widget.m_LastNewEmision)); CPPUNIT_ASSERT(EqualNodeSelections({}, widget.GetSelectedNodes())); } void NodePredicateTest() { TestWidget widget; CPPUNIT_ASSE