diff --git a/CMake/mitkFunctionAddCustomModuleTest.cmake b/CMake/mitkFunctionAddCustomModuleTest.cmake index 4659520d33..3c57b8cfd3 100644 --- a/CMake/mitkFunctionAddCustomModuleTest.cmake +++ b/CMake/mitkFunctionAddCustomModuleTest.cmake @@ -1,28 +1,28 @@ #! #! \brief Add a custom test for MITK module #! #! \param test_name Unique identifier for the test #! \param test_function Name of the test function (the one with the argc,argv signature) #! #! Additional parameters will be passed as command line parameters to the test. #! function(mitkAddCustomModuleTest test_name test_function) if (BUILD_TESTING AND MODULE_IS_ENABLED) if(MITK_XVFB_TESTING) - set(xvfb_run "xvfb-run" "--auto-servernum") + set(xvfb_run ${MITK_XVFB_TESTING_COMMAND}) else() set(xvfb_run ) endif() add_test(NAME ${test_name} COMMAND ${xvfb_run} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} ${test_function} ${ARGN}) set_property(TEST ${test_name} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) mitkFunctionGetLibrarySearchPaths(MITK_RUNTIME_PATH_RELEASE release RELEASE) mitkFunctionGetLibrarySearchPaths(MITK_RUNTIME_PATH_DEBUG debug DEBUG) set(test_env_path ${MITK_RUNTIME_PATH_RELEASE} ${MITK_RUNTIME_PATH_DEBUG} $ENV{PATH}) list(REMOVE_DUPLICATES test_env_path) string (REGEX REPLACE "\;" "\\\;" test_env_path "${test_env_path}") set_property(TEST ${test_name} PROPERTY ENVIRONMENT "PATH=${test_env_path}" APPEND) set_property(TEST ${test_name} PROPERTY SKIP_RETURN_CODE 77) endif() endfunction() diff --git a/CMake/mitkFunctionCreateModule.cmake b/CMake/mitkFunctionCreateModule.cmake index 8f753d0433..5a4205ba7b 100644 --- a/CMake/mitkFunctionCreateModule.cmake +++ b/CMake/mitkFunctionCreateModule.cmake @@ -1,674 +1,675 @@ ################################################################## # # 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) 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) - set_property(TARGET ${MODULE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules") + # 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/CMake/mitkMacroCreateModuleTests.cmake b/CMake/mitkMacroCreateModuleTests.cmake index 9c6c81a7f3..a60564cac2 100644 --- a/CMake/mitkMacroCreateModuleTests.cmake +++ b/CMake/mitkMacroCreateModuleTests.cmake @@ -1,109 +1,109 @@ # # Create tests and testdriver for this module # # Usage: MITK_CREATE_MODULE_TESTS( [EXTRA_DRIVER_INIT init_code] ) # # EXTRA_DRIVER_INIT is inserted as c++ code in the testdriver and will be executed before each test # macro(MITK_CREATE_MODULE_TESTS) cmake_parse_arguments(MODULE_TEST "US_MODULE;NO_INIT" "EXTRA_DRIVER_INIT;EXTRA_DRIVER_INCLUDE" "EXTRA_DEPENDS;DEPENDS;PACKAGE_DEPENDS" ${ARGN}) if(BUILD_TESTING AND MODULE_IS_ENABLED) include(files.cmake) include_directories(.) set(TESTDRIVER ${MODULE_NAME}TestDriver) set(MODULE_TEST_EXTRA_DRIVER_INIT "${MODULE_TEST_EXTRA_DRIVER_INIT}") if(MITK_XVFB_TESTING) - set(xvfb_run "xvfb-run" "--auto-servernum") + set(xvfb_run ${MITK_XVFB_TESTING_COMMAND}) else() set(xvfb_run ) endif() if(MODULE_TEST_US_MODULE) message(WARNING "The US_MODULE argument is deprecated and should be removed") endif() if(MODULE_TEST_US_MODULE AND MODULE_TEST_NO_INIT) message(WARNING "Conflicting arguments US_MODULE and NO_INIT: NO_INIT wins.") endif() set(_no_init) if(MODULE_TEST_NO_INIT) set(_no_init NO_INIT) endif() set(MITK_MODULE_NAME_REGEX_MATCH ) set(MITK_MODULE_NAME_REGEX_NOT_MATCH ) set(_testdriver_file_list ${CMAKE_CURRENT_BINARY_DIR}/testdriver_files.cmake) configure_file(${MITK_CMAKE_DIR}/mitkTestDriverFiles.cmake.in ${_testdriver_file_list} @ONLY) mitk_create_executable(${TESTDRIVER} DEPENDS ${MODULE_NAME} ${MODULE_TEST_DEPENDS} ${MODULE_TEST_EXTRA_DEPENDS} MitkTestingHelper PACKAGE_DEPENDS ${MODULE_TEST_PACKAGE_DEPENDS} SUBPROJECTS ${MODULE_SUBPROJECTS} FILES_CMAKE ${_testdriver_file_list} NO_FEATURE_INFO NO_BATCH_FILE NO_INSTALL ${_no_init}) set_property(TARGET ${EXECUTABLE_TARGET} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules/Tests") # # Now tell CMake which tests should be run. This is done automatically # for all tests in ${KITNAME}_TESTS and ${KITNAME}_IMAGE_TESTS. The IMAGE_TESTS # are run for each image in the TESTIMAGES list. # include(files.cmake) foreach( test ${MODULE_TESTS} ) get_filename_component(TName ${test} NAME_WE) add_test(NAME ${TName} COMMAND ${xvfb_run} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} ${TName}) # Add labels for CDash subproject support if(MODULE_SUBPROJECTS) set_property(TEST ${TName} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) endif() mitkFunctionGetLibrarySearchPaths(MITK_RUNTIME_PATH_RELEASE release RELEASE) mitkFunctionGetLibrarySearchPaths(MITK_RUNTIME_PATH_DEBUG debug DEBUG) set(test_env_path ${MITK_RUNTIME_PATH_RELEASE} ${MITK_RUNTIME_PATH_DEBUG} $ENV{PATH}) list(REMOVE_DUPLICATES test_env_path) string (REGEX REPLACE "\;" "\\\;" test_env_path "${test_env_path}") set_property(TEST ${TName} PROPERTY ENVIRONMENT "PATH=${test_env_path}" APPEND) set_property(TEST ${TName} PROPERTY SKIP_RETURN_CODE 77) endforeach() set(TEST_TYPES IMAGE SURFACE POINTSET) # add other file types here foreach(test_type ${TEST_TYPES}) foreach(test_data ${MODULE_TEST${test_type}} ${ADDITIONAL_TEST_${test_type}}) if(EXISTS ${test_data}) set(TEST_DATA_FULL_PATH ${test_data}) else() # todo: maybe search other paths as well # yes, please in mitk/Testing/Data, too set(TEST_DATA_FULL_PATH ${MITK_DATA_DIR}/${test_data}) endif() if(EXISTS ${TEST_DATA_FULL_PATH}) foreach( test ${MODULE_${test_type}_TESTS}) get_filename_component(TName ${test} NAME_WE) get_filename_component(DName ${TEST_DATA_FULL_PATH} NAME) add_test(NAME ${TName}_${DName} COMMAND ${xvfb_run} ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${TESTDRIVER} ${TName} ${TEST_DATA_FULL_PATH}) # Add labels for CDash subproject support if(MODULE_SUBPROJECTS) set_property(TEST ${TName}_${DName} PROPERTY LABELS ${MODULE_SUBPROJECTS} MITK) endif() set_property(TEST ${TName}_${DName} PROPERTY ENVIRONMENT "PATH=${test_env_path}" APPEND) set_property(TEST ${TName}_${DName} PROPERTY SKIP_RETURN_CODE 77) endforeach() else() message("!!!!! No such file: ${TEST_DATA_FULL_PATH} !!!!!") endif() endforeach() endforeach() endif() endmacro() diff --git a/CMake/mitkPackageTest.cmake b/CMake/mitkPackageTest.cmake index 6471278a3e..615870dc1f 100644 --- a/CMake/mitkPackageTest.cmake +++ b/CMake/mitkPackageTest.cmake @@ -1,26 +1,26 @@ if(BUILD_TESTING) #package testing if(NOT MITK_FAST_TESTING) # package testing in windows only for release if(WIN32) add_test(NAME mitkPackageTest CONFIGURATIONS Release COMMAND ${CMAKE_COMMAND} --build ${MITK_BINARY_DIR} --config Release --target package) set_tests_properties( mitkPackageTest PROPERTIES - TIMEOUT 6000 + TIMEOUT 14400 LABELS "MITK;MITK-Plugins;PACKAGE_TESTS") elseif(CMAKE_BUILD_TYPE) add_test( NAME mitkPackageTest COMMAND ${CMAKE_COMMAND} --build ${MITK_BINARY_DIR} --config ${CMAKE_BUILD_TYPE} --target package) set_tests_properties( mitkPackageTest PROPERTIES - TIMEOUT 6000 + TIMEOUT 14400 LABELS "MITK;MITK-Plugins;PACKAGE_TESTS" RUN_SERIAL TRUE) endif() endif() # NOT MITK_FAST_TESTING endif(BUILD_TESTING) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0431e192ed..1ae49aa27d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,1473 +1,1481 @@ set(MITK_CMAKE_MINIMUM_REQUIRED_VERSION 3.14.5) cmake_minimum_required(VERSION ${MITK_CMAKE_MINIMUM_REQUIRED_VERSION}) #----------------------------------------------------------------------------- # See https://cmake.org/cmake/help/v3.14/manual/cmake-policies.7.html for details #----------------------------------------------------------------------------- set(project_policies ) foreach(policy ${project_policies}) if(POLICY ${policy}) cmake_policy(SET ${policy} NEW) endif() endforeach() #----------------------------------------------------------------------------- # Superbuild Option - Enabled by default #----------------------------------------------------------------------------- option(MITK_USE_SUPERBUILD "Build MITK and the projects it depends on via SuperBuild.cmake." ON) if(MITK_USE_SUPERBUILD) project(MITK-superbuild) set(MITK_SOURCE_DIR ${PROJECT_SOURCE_DIR}) set(MITK_BINARY_DIR ${PROJECT_BINARY_DIR}) else() project(MITK VERSION 2018.04.99) include_directories(SYSTEM ${MITK_SUPERBUILD_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # MITK Extension Feature #----------------------------------------------------------------------------- set(MITK_EXTENSION_DIRS "" CACHE STRING "") set(MITK_DIR_PLUS_EXTENSION_DIRS ${MITK_SOURCE_DIR} ${MITK_EXTENSION_DIRS}) #----------------------------------------------------------------------------- # Update CMake module path #----------------------------------------------------------------------------- set(MITK_CMAKE_DIR ${MITK_SOURCE_DIR}/CMake) set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMake) get_filename_component(MITK_CMAKE_EXTENSION_DIR ${MITK_CMAKE_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_CMAKE_EXTENSION_DIR}) list(APPEND CMAKE_MODULE_PATH ${MITK_CMAKE_EXTENSION_DIR}) endif() endforeach() #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- # Standard CMake macros include(FeatureSummary) include(CTestUseLaunchers) include(CMakeParseArguments) include(FindPackageHandleStandardArgs) # MITK macros include(mitkFunctionGetGccVersion) include(mitkFunctionCheckCompilerFlags) include(mitkFunctionSuppressWarnings) # includes several functions include(mitkMacroEmptyExternalProject) include(mitkFunctionGenerateProjectXml) include(mitkFunctionEnableBuildConfiguration) include(mitkFunctionWhitelists) include(mitkFunctionAddExternalProject) include(mitkFunctionAddLibrarySearchPaths) SUPPRESS_VC_DEPRECATED_WARNINGS() #----------------------------------------------------------------------------- # Set a default build type if none was specified #----------------------------------------------------------------------------- if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) message(STATUS "Setting build type to 'Debug' as none was specified.") set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build." FORCE) # Set the possible values of build type for cmake-gui set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" "RelWithDebInfo") endif() #----------------------------------------------------------------------------- # Check miminum macOS version #----------------------------------------------------------------------------- # The minimum supported macOS version is 10.13. If you use a version less than 10.13, there is no guarantee that the build still works. if(APPLE) exec_program(sw_vers ARGS -productVersion OUTPUT_VARIABLE macos_version) if (macos_version VERSION_LESS "10.13") message(WARNING "Detected macOS version \"${macos_version}\" is not supported anymore. Minimum required macOS version is at least 10.13.") endif() if (CMAKE_OSX_DEPLOYMENT_TARGET AND CMAKE_OSX_DEPLOYMENT_TARGET VERSION_LESS 10.13) message(WARNING "Detected macOS deployment target \"${CMAKE_OSX_DEPLOYMENT_TARGET}\" is not supported anymore. Minimum required macOS version is at least 10.13.") endif() endif() #----------------------------------------------------------------------------- # Check miminum compiler versions #----------------------------------------------------------------------------- if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") # require at least gcc 4.9 as provided by ppa:ubuntu-toolchain-r/test for Ubuntu 14.04 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 4.9) message(FATAL_ERROR "GCC version must be at least 4.9 If you are using Ubuntu 14.04, you can easily install gcc and g++ 4.9 (or any later version available) in addition to your version ${CMAKE_CXX_COMPILER_VERSION}: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt-get update sudo apt-get install gcc-4.9 g++-4.9 Make sure to explicitly specify these compilers when configuring MITK: CMAKE_C_COMPILER:FILEPATH=/usr/bin/gcc-4.9 CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++-4.9 For more information on the proposed PPA see the Toolchain Updates section of https://wiki.ubuntu.com/ToolChain.") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") # require at least clang 3.4 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 3.4) message(FATAL_ERROR "Clang version must be at least 3.4") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "AppleClang") # require at least clang 5.0 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 5.0) message(FATAL_ERROR "Apple Clang version must be at least 5.0") endif() elseif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC") # require at least Visual Studio 2017 if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 19.10) message(FATAL_ERROR "Microsoft Visual Studio 2017 or newer required") endif() else() message(WARNING "You are using an unsupported compiler! Compilation has only been tested with Clang (Linux or Apple), GCC and MSVC.") endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionGetGccVersion(${CMAKE_CXX_COMPILER} GCC_VERSION) else() set(GCC_VERSION 0) endif() set(MITK_CXX_STANDARD 14) set(CMAKE_CXX_EXTENSIONS 0) set(CMAKE_CXX_STANDARD ${MITK_CXX_STANDARD}) set(CMAKE_CXX_STANDARD_REQUIRED 1) # This is necessary to avoid problems with compile feature checks. # CMAKE_CXX_STANDARD seems to only set the -std=c++14 flag for targets. # However, compile flag checks also need to be done with -std=c++14. # The MITK_CXX14_FLAG variable is also used for external projects # build during the MITK super-build. mitkFunctionCheckCompilerFlags("-std=c++14" MITK_CXX14_FLAG) #----------------------------------------------------------------------------- # Warn if source or build path is too long #----------------------------------------------------------------------------- if(WIN32) set(_src_dir_length_max 50) set(_bin_dir_length_max 50) if(MITK_USE_SUPERBUILD) set(_src_dir_length_max 34) # _src_dir_length_max - strlen(ep/src/ITK-build) set(_bin_dir_length_max 40) # _bin_dir_length_max - strlen(MITK-build) endif() string(LENGTH "${MITK_SOURCE_DIR}" _src_n) string(LENGTH "${MITK_BINARY_DIR}" _bin_n) # The warnings should be converted to errors if(_src_n GREATER _src_dir_length_max) message(WARNING "MITK source code directory path length is too long (${_src_n} > ${_src_dir_length_max})." "Please move the MITK source code directory to a directory with a shorter path." ) endif() if(_bin_n GREATER _bin_dir_length_max) message(WARNING "MITK build directory path length is too long (${_bin_n} > ${_bin_dir_length_max})." "Please move the MITK build directory to a directory with a shorter path." ) endif() endif() #----------------------------------------------------------------------------- # Additional MITK Options (also shown during superbuild) #----------------------------------------------------------------------------- # ----------------------------------------- # General build options option(BUILD_SHARED_LIBS "Build MITK with shared libraries" ON) option(WITH_COVERAGE "Enable/Disable coverage" OFF) option(BUILD_TESTING "Test the project" ON) option(MITK_FAST_TESTING "Disable long-running tests like packaging" OFF) option(MITK_XVFB_TESTING "Execute test drivers through xvfb-run" OFF) option(MITK_BUILD_ALL_APPS "Build all MITK applications" OFF) option(MITK_BUILD_EXAMPLES "Build the MITK Examples" OFF) option(MITK_ENABLE_PIC_READER "Enable support for reading the DKFZ pic file format." ON) mark_as_advanced( MITK_XVFB_TESTING MITK_FAST_TESTING MITK_BUILD_ALL_APPS MITK_ENABLE_PIC_READER ) +#----------------------------------------------------------------------------- +# Set UI testing flags +#----------------------------------------------------------------------------- +if(MITK_XVFB_TESTING) + set(MITK_XVFB_TESTING_COMMAND "xvfb-run" "--auto-servernum" CACHE STRING "Command and options to test through Xvfb") + mark_as_advanced(MITK_XVFB_TESTING_COMMAND) +endif(MITK_XVFB_TESTING) + # ----------------------------------------- # Other options set(MITK_CUSTOM_REVISION_DESC "" CACHE STRING "Override MITK revision description") mark_as_advanced(MITK_CUSTOM_REVISION_DESC) set_property(GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS "") include(CMakeExternals/ExternalProjectList.cmake) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMakeExternals) get_filename_component(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_CMAKE_EXTERNALS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake) include(${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/ExternalProjectList.cmake) endif() endforeach() # ----------------------------------------- # Other MITK_USE_* options not related to # external projects build via the # MITK superbuild option(MITK_USE_BLUEBERRY "Build the BlueBerry platform" ON) option(MITK_USE_OpenCL "Use OpenCL GPU-Computing library" OFF) option(MITK_USE_OpenMP "Use OpenMP" OFF) option(MITK_USE_Python3 "Use Python 3" OFF) #----------------------------------------------------------------------------- # Build configurations #----------------------------------------------------------------------------- set(_buildConfigs "Custom") file(GLOB _buildConfigFiles CMake/BuildConfigurations/*.cmake) foreach(_buildConfigFile ${_buildConfigFiles}) get_filename_component(_buildConfigFile ${_buildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_buildConfigFile}) endforeach() foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) file(GLOB _extBuildConfigFiles ${MITK_EXTENSION_DIR}/CMake/BuildConfigurations/*.cmake) foreach(_extBuildConfigFile ${_extBuildConfigFiles}) get_filename_component(_extBuildConfigFile ${_extBuildConfigFile} NAME_WE) list(APPEND _buildConfigs ${_extBuildConfigFile}) endforeach() list(REMOVE_DUPLICATES _buildConfigs) endforeach() set(MITK_BUILD_CONFIGURATION "Custom" CACHE STRING "Use pre-defined MITK configurations") set_property(CACHE MITK_BUILD_CONFIGURATION PROPERTY STRINGS ${_buildConfigs}) mitkFunctionEnableBuildConfiguration() mitkFunctionCreateWhitelistPaths(MITK) mitkFunctionFindWhitelists(MITK) # ----------------------------------------- # Qt version related variables option(MITK_USE_Qt5 "Use Qt 5 library" ON) if(MITK_USE_Qt5) set(MITK_QT5_MINIMUM_VERSION 5.12) set(MITK_QT5_COMPONENTS Concurrent OpenGL PrintSupport Script Sql Svg Widgets Xml XmlPatterns WebEngineWidgets UiTools Help LinguistTools) if(APPLE) list(APPEND MITK_QT5_COMPONENTS DBus) elseif(UNIX) list(APPEND MITK_QT5_COMPONENTS X11Extras) endif() # Hint at default install locations of Qt if(NOT Qt5_DIR) if(MSVC) set(_dir_candidates "C:/Qt") if(CMAKE_GENERATOR MATCHES "^Visual Studio [0-9]+ ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") elseif(CMAKE_GENERATOR MATCHES "Ninja") include(mitkFunctionGetMSVCVersion) mitkFunctionGetMSVCVersion() if(VISUAL_STUDIO_PRODUCT_NAME MATCHES "^Visual Studio ([0-9]+)") set(_compilers "msvc${CMAKE_MATCH_1}") endif() endif() if(_compilers MATCHES "[0-9]+") if (CMAKE_MATCH_0 EQUAL 2019) list(APPEND _compilers "msvc2017") # Binary compatible to 2019 endif() endif() else() set(_dir_candidates ~/Qt) if(APPLE) set(_compilers clang) else() list(APPEND _dir_candidates /opt/Qt) set(_compilers gcc) endif() endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) foreach(_compiler ${_compilers}) list(APPEND _compilers64 "${_compiler}_64") endforeach() set(_compilers ${_compilers64}) endif() foreach(_dir_candidate ${_dir_candidates}) get_filename_component(_dir_candidate ${_dir_candidate} REALPATH) foreach(_compiler ${_compilers}) set(_glob_expression "${_dir_candidate}/5.*/${_compiler}") file(GLOB _hints ${_glob_expression}) list(SORT _hints) list(APPEND MITK_QT5_HINTS ${_hints}) endforeach() endforeach() endif() find_package(Qt5 ${MITK_QT5_MINIMUM_VERSION} COMPONENTS ${MITK_QT5_COMPONENTS} REQUIRED HINTS ${MITK_QT5_HINTS}) if(${Qt5_VERSION} VERSION_GREATER_EQUAL 5.13) message(FATAL_ERROR "Qt version ${Qt5_VERSION_MAJOR}.${Qt5_VERSION_MINOR} is not yet supported. We recommend using the latest long-term support version 5.12.x.") endif() endif() # ----------------------------------------- # Custom dependency logic option(MITK_USE_SYSTEM_Boost "Use the system Boost" OFF) set(MITK_USE_Boost_LIBRARIES "" CACHE STRING "A semi-colon separated list of required Boost libraries") if(MITK_USE_cpprestsdk) find_package(OpenSSL QUIET) if(NOT OpenSSL_FOUND) set(openssl_message "Could not find OpenSSL (dependency of C++ REST SDK).\n") if(UNIX) if(APPLE) set(openssl_message "${openssl_message}Please install it using your favorite package management " "system (i.e. Homebrew or MacPorts).\n") else() set(openssl_message "${openssl_message}Please install the dev package of OpenSSL (i.e. libssl-dev).\n") endif() else() set(openssl_message "${openssl_message}Please install Win32 OpenSSL:\n" " https://slproweb.com/products/Win32OpenSSL.html\n") endif() set(openssl_message "${openssl_message}If it still cannot be found, you can hint CMake to find OpenSSL by " "adding/setting the OPENSSL_ROOT_DIR variable to the root directory of an " "OpenSSL installation. Make sure to clear variables of partly found " "versions of OpenSSL before, or they will be mixed up.") message(FATAL_ERROR ${openssl_message}) endif() list(APPEND MITK_USE_Boost_LIBRARIES date_time regex system) if(UNIX) list(APPEND MITK_USE_Boost_LIBRARIES atomic chrono filesystem random thread) endif() list(REMOVE_DUPLICATES MITK_USE_Boost_LIBRARIES) set(MITK_USE_Boost_LIBRARIES ${MITK_USE_Boost_LIBRARIES} CACHE STRING "A semi-colon separated list of required Boost libraries" FORCE) endif() if(MITK_USE_Python3) set(MITK_USE_ZLIB ON CACHE BOOL "" FORCE) if(APPLE AND CMAKE_FRAMEWORK_PATH AND CMAKE_FRAMEWORK_PATH MATCHES "python3\\.?([0-9]+)") find_package(Python3 3.${CMAKE_MATCH_1} EXACT REQUIRED COMPONENTS Interpreter Development NumPy) else() find_package(Python3 REQUIRED COMPONENTS Interpreter Development NumPy) endif() if(WIN32) string(REPLACE "\\" "/" Python3_STDARCH "${Python3_STDARCH}") string(REPLACE "\\" "/" Python3_STDLIB "${Python3_STDLIB}") string(REPLACE "\\" "/" Python3_SITELIB "${Python3_SITELIB}") endif() endif() if(BUILD_TESTING AND NOT MITK_USE_CppUnit) message("> Forcing MITK_USE_CppUnit to ON because BUILD_TESTING=ON") set(MITK_USE_CppUnit ON CACHE BOOL "Use CppUnit for unit tests" FORCE) endif() if(MITK_USE_BLUEBERRY) option(MITK_BUILD_ALL_PLUGINS "Build all MITK plugins" OFF) mark_as_advanced(MITK_BUILD_ALL_PLUGINS) if(NOT MITK_USE_CTK) message("> Forcing MITK_USE_CTK to ON because of MITK_USE_BLUEBERRY") set(MITK_USE_CTK ON CACHE BOOL "Use CTK in MITK" FORCE) endif() endif() #----------------------------------------------------------------------------- # Pixel type multiplexing #----------------------------------------------------------------------------- # Customize the default pixel types for multiplex macros set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros") set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") mark_as_advanced(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES MITK_ACCESSBYITK_DIMENSIONS ) # consistency checks if(NOT MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES) set(MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES "int, unsigned int, short, unsigned short, char, unsigned char" CACHE STRING "List of integral pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES) set(MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES "double, float" CACHE STRING "List of floating pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES) set(MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES "itk::RGBPixel, itk::RGBAPixel" CACHE STRING "List of composite pixel types used in AccessByItk and InstantiateAccessFunction macros" FORCE) endif() if(NOT MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) string(REPLACE "," ";" _integral_types ${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES}) string(REPLACE "," ";" _floating_types ${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES}) foreach(_scalar_type ${_integral_types} ${_floating_types}) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}itk::VariableLengthVector<${_scalar_type}>,") endforeach() string(LENGTH "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" _length) math(EXPR _length "${_length} - 1") string(SUBSTRING "${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES}" 0 ${_length} MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES) set(MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES ${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} CACHE STRING "List of vector pixel types used in AccessByItk and InstantiateAccessFunction macros for itk::VectorImage types" FORCE) endif() if(NOT MITK_ACCESSBYITK_DIMENSIONS) set(MITK_ACCESSBYITK_DIMENSIONS "2,3" CACHE STRING "List of dimensions used in AccessByItk and InstantiateAccessFunction macros") endif() #----------------------------------------------------------------------------- # Project.xml #----------------------------------------------------------------------------- # A list of topologically ordered targets set(CTEST_PROJECT_SUBPROJECTS) list(APPEND CTEST_PROJECT_SUBPROJECTS MITK-Core MITK-CoreUI MITK-IGT MITK-ToF MITK-Modules # all modules not contained in a specific subproject MITK-Plugins # all plugins not contained in a specific subproject MITK-Examples Unlabeled # special "subproject" catching all unlabeled targets and tests ) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_CMAKE_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMake) get_filename_component(MITK_CMAKE_EXTENSION_DIR ${MITK_CMAKE_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_CMAKE_EXTENSION_DIR}/CTestSubprojectList.cmake) set(MITK_CTEST_SUBPROJECTS "") include(${MITK_CMAKE_EXTENSION_DIR}/CTestSubprojectList.cmake) if(MITK_CTEST_SUBPROJECTS) list(APPEND CTEST_PROJECT_SUBPROJECTS ${MITK_CTEST_SUBPROJECTS}) endif() endif() endforeach() # Configure CTestConfigSubProject.cmake that could be used by CTest scripts configure_file(${MITK_SOURCE_DIR}/CTestConfigSubProject.cmake.in ${MITK_BINARY_DIR}/CTestConfigSubProject.cmake) if(CTEST_PROJECT_ADDITIONAL_TARGETS) # those targets will be executed at the end of the ctest driver script # and they also get their own subproject label set(subproject_list "${CTEST_PROJECT_SUBPROJECTS};${CTEST_PROJECT_ADDITIONAL_TARGETS}") else() set(subproject_list "${CTEST_PROJECT_SUBPROJECTS}") endif() # Generate Project.xml file expected by the CTest driver script mitkFunctionGenerateProjectXml(${MITK_BINARY_DIR} MITK "${subproject_list}" ${MITK_USE_SUPERBUILD}) #----------------------------------------------------------------------------- # Superbuild script #----------------------------------------------------------------------------- if(MITK_USE_SUPERBUILD) include("${CMAKE_CURRENT_SOURCE_DIR}/SuperBuild.cmake") # Print configuration summary message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL) return() endif() #***************************************************************************** #**************************** END OF SUPERBUILD **************************** #***************************************************************************** #----------------------------------------------------------------------------- # Organize MITK targets in folders #----------------------------------------------------------------------------- set_property(GLOBAL PROPERTY USE_FOLDERS ON) set(MITK_ROOT_FOLDER "MITK" CACHE STRING "") mark_as_advanced(MITK_ROOT_FOLDER) #----------------------------------------------------------------------------- # CMake function(s) and macro(s) #----------------------------------------------------------------------------- include(WriteBasicConfigVersionFile) include(CheckCXXSourceCompiles) include(GenerateExportHeader) include(mitkFunctionAddCustomModuleTest) include(mitkFunctionCheckModuleDependencies) include(mitkFunctionCompileSnippets) include(mitkFunctionConfigureVisualStudioUserProjectFile) include(mitkFunctionConvertXPSchema) include(mitkFunctionCreateBlueBerryApplication) include(mitkFunctionCreateCommandLineApp) include(mitkFunctionCreateModule) include(mitkFunctionCreatePlugin) include(mitkFunctionCreateProvisioningFile) include(mitkFunctionGetLibrarySearchPaths) include(mitkFunctionGetVersion) include(mitkFunctionGetVersionDescription) include(mitkFunctionInstallAutoLoadModules) include(mitkFunctionInstallCTKPlugin) include(mitkFunctionInstallProvisioningFiles) include(mitkFunctionInstallThirdPartyCTKPlugins) include(mitkFunctionOrganizeSources) include(mitkFunctionUseModules) if( ${MITK_USE_MatchPoint} ) include(mitkFunctionCreateMatchPointDeployedAlgorithm) endif() include(mitkMacroConfigureItkPixelTypes) include(mitkMacroCreateExecutable) include(mitkMacroCreateModuleTests) include(mitkMacroGenerateToolsLibrary) include(mitkMacroGetLinuxDistribution) include(mitkMacroGetPMDPlatformString) include(mitkMacroInstall) include(mitkMacroInstallHelperApp) include(mitkMacroInstallTargets) include(mitkMacroMultiplexPicType) # Deprecated include(mitkMacroCreateCTKPlugin) #----------------------------------------------------------------------------- # Global CMake variables #----------------------------------------------------------------------------- # Required and enabled C++14 features for all MITK code. # These are added as PUBLIC compile features to all MITK modules. set(MITK_CXX_FEATURES cxx_auto_type cxx_decltype cxx_enum_forward_declarations cxx_extended_friend_declarations cxx_extern_templates cxx_final cxx_lambdas cxx_local_type_template_args cxx_long_long_type cxx_nullptr cxx_override cxx_range_for cxx_right_angle_brackets cxx_rvalue_references cxx_static_assert cxx_strong_enums cxx_template_template_parameters cxx_trailing_return_types cxx_variadic_macros ) if(NOT DEFINED CMAKE_DEBUG_POSTFIX) # We can't do this yet because the CTK Plugin Framework # cannot cope with a postfix yet. #set(CMAKE_DEBUG_POSTFIX d) endif() #----------------------------------------------------------------------------- # Output directories. #----------------------------------------------------------------------------- set(_default_LIBRARY_output_dir lib) set(_default_RUNTIME_output_dir bin) set(_default_ARCHIVE_output_dir lib) foreach(type LIBRARY RUNTIME ARCHIVE) # Make sure the directory exists if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY AND NOT EXISTS ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) message("Creating directory MITK_CMAKE_${type}_OUTPUT_DIRECTORY: ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") file(MAKE_DIRECTORY "${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}") endif() if(MITK_CMAKE_${type}_OUTPUT_DIRECTORY) set(CMAKE_${type}_OUTPUT_DIRECTORY ${MITK_CMAKE_${type}_OUTPUT_DIRECTORY}) else() set(CMAKE_${type}_OUTPUT_DIRECTORY ${PROJECT_BINARY_DIR}/${_default_${type}_output_dir}) set(MITK_CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY}) endif() set(CMAKE_${type}_OUTPUT_DIRECTORY ${CMAKE_${type}_OUTPUT_DIRECTORY} CACHE INTERNAL "Output directory for ${type} files.") mark_as_advanced(CMAKE_${type}_OUTPUT_DIRECTORY) endforeach() #----------------------------------------------------------------------------- # Set MITK specific options and variables (NOT available during superbuild) #----------------------------------------------------------------------------- # Look for optional Doxygen package find_package(Doxygen) option(BLUEBERRY_DEBUG_SMARTPOINTER "Enable code for debugging smart pointers" OFF) mark_as_advanced(BLUEBERRY_DEBUG_SMARTPOINTER) # Ask the user to show the console window for applications option(MITK_SHOW_CONSOLE_WINDOW "Use this to enable or disable the console window when starting MITK GUI Applications" ON) mark_as_advanced(MITK_SHOW_CONSOLE_WINDOW) # TODO: check if necessary option(USE_ITKZLIB "Use the ITK zlib for pic compression." ON) mark_as_advanced(USE_ITKZLIB) if(NOT MITK_FAST_TESTING) if(DEFINED MITK_CTEST_SCRIPT_MODE AND (MITK_CTEST_SCRIPT_MODE STREQUAL "continuous" OR MITK_CTEST_SCRIPT_MODE STREQUAL "experimental") ) set(MITK_FAST_TESTING ON) endif() endif() if(NOT UNIX) set(MITK_WIN32_FORCE_STATIC "STATIC" CACHE INTERNAL "Use this variable to always build static libraries on non-unix platforms") endif() if(MITK_BUILD_ALL_PLUGINS) set(MITK_BUILD_ALL_PLUGINS_OPTION "FORCE_BUILD_ALL") endif() # Configure pixel types used for ITK image access multiplexing mitkMacroConfigureItkPixelTypes() # Configure module naming conventions set(MITK_MODULE_NAME_REGEX_MATCH "^[A-Z].*$") set(MITK_MODULE_NAME_REGEX_NOT_MATCH "^[Mm][Ii][Tt][Kk].*$") set(MITK_DEFAULT_MODULE_NAME_PREFIX "Mitk") set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) set(MITK_MODULE_NAME_DEFAULTS_TO_DIRECTORY_NAME 1) #----------------------------------------------------------------------------- # Get MITK version info #----------------------------------------------------------------------------- mitkFunctionGetVersion(${MITK_SOURCE_DIR} MITK) mitkFunctionGetVersionDescription(${MITK_SOURCE_DIR} MITK) # MITK_VERSION set(MITK_VERSION_STRING "${MITK_VERSION_MAJOR}.${MITK_VERSION_MINOR}.${MITK_VERSION_PATCH}") if(MITK_VERSION_PATCH STREQUAL "99") set(MITK_VERSION_STRING "${MITK_VERSION_STRING}-${MITK_REVISION_SHORTID}") endif() #----------------------------------------------------------------------------- # Installation preparation # # These should be set before any MITK install macros are used #----------------------------------------------------------------------------- # on macOS all BlueBerry plugins get copied into every # application bundle (.app directory) specified here if(MITK_USE_BLUEBERRY AND APPLE) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) set(MITK_APPS "") include(${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 1 option_name) list(GET target_info_list 0 app_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) set(MACOSX_BUNDLE_NAMES ${MACOSX_BUNDLE_NAMES} Mitk${app_name}) endif() endforeach() endif() endforeach() endif() #----------------------------------------------------------------------------- # Set coverage Flags #----------------------------------------------------------------------------- if(WITH_COVERAGE) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") set(coverage_flags "-g -fprofile-arcs -ftest-coverage -O0 -DNDEBUG") set(COVERAGE_CXX_FLAGS ${coverage_flags}) set(COVERAGE_C_FLAGS ${coverage_flags}) endif() endif() #----------------------------------------------------------------------------- # MITK C/CXX Flags #----------------------------------------------------------------------------- set(MITK_C_FLAGS "${COVERAGE_C_FLAGS}") set(MITK_C_FLAGS_DEBUG ) set(MITK_C_FLAGS_RELEASE ) set(MITK_CXX_FLAGS "${COVERAGE_CXX_FLAGS} ${MITK_CXX14_FLAG}") set(MITK_CXX_FLAGS_DEBUG ) set(MITK_CXX_FLAGS_RELEASE ) set(MITK_EXE_LINKER_FLAGS ) set(MITK_SHARED_LINKER_FLAGS ) if(WIN32) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DPOCO_NO_UNWINDOWS -DWIN32_LEAN_AND_MEAN -DNOMINMAX") mitkFunctionCheckCompilerFlags("/wd4005" MITK_CXX_FLAGS) # warning C4005: macro redefinition mitkFunctionCheckCompilerFlags("/wd4231" MITK_CXX_FLAGS) # warning C4231: nonstandard extension used : 'extern' before template explicit instantiation # the following line should be removed after fixing bug 17637 mitkFunctionCheckCompilerFlags("/wd4316" MITK_CXX_FLAGS) # warning C4316: object alignment on heap mitkFunctionCheckCompilerFlags("/wd4180" MITK_CXX_FLAGS) # warning C4180: qualifier applied to function type has no meaning mitkFunctionCheckCompilerFlags("/wd4251" MITK_CXX_FLAGS) # warning C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2' endif() if(APPLE) set(MITK_CXX_FLAGS "${MITK_CXX_FLAGS} -DGL_SILENCE_DEPRECATION") # Apple deprecated OpenGL in macOS 10.14 endif() if(NOT MSVC_VERSION) foreach(_flag -Wall -Wextra -Wpointer-arith -Winvalid-pch -Wcast-align -Wwrite-strings -Wno-error=gnu -Wno-error=unknown-pragmas # The strict-overflow warning is generated by ITK template code -Wno-error=strict-overflow -Woverloaded-virtual -Wstrict-null-sentinel #-Wold-style-cast #-Wsign-promo -Wno-error=deprecated-copy -Wno-array-bounds -fdiagnostics-show-option ) mitkFunctionCheckCAndCXXCompilerFlags(${_flag} MITK_C_FLAGS MITK_CXX_FLAGS) endforeach() endif() if(CMAKE_COMPILER_IS_GNUCXX AND NOT APPLE) mitkFunctionCheckCompilerFlags("-Wl,--no-undefined" MITK_SHARED_LINKER_FLAGS) mitkFunctionCheckCompilerFlags("-Wl,--as-needed" MITK_SHARED_LINKER_FLAGS) endif() if(CMAKE_COMPILER_IS_GNUCXX) mitkFunctionCheckCAndCXXCompilerFlags("-fstack-protector-all" MITK_C_FLAGS MITK_CXX_FLAGS) set(MITK_CXX_FLAGS_RELEASE "-U_FORTIFY_SOURCES -D_FORTIFY_SOURCE=2 ${MITK_CXX_FLAGS_RELEASE}") endif() set(MITK_MODULE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) set(MITK_EXE_LINKER_FLAGS ${MITK_SHARED_LINKER_FLAGS}) #----------------------------------------------------------------------------- # MITK Packages #----------------------------------------------------------------------------- set(MITK_MODULES_PACKAGE_DEPENDS_DIR ${MITK_SOURCE_DIR}/CMake/PackageDepends) set(MODULES_PACKAGE_DEPENDS_DIRS ${MITK_MODULES_PACKAGE_DEPENDS_DIR}) foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_PACKAGE_DEPENDS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMake/PackageDepends) get_filename_component(MITK_PACKAGE_DEPENDS_EXTENSION_DIR ${MITK_PACKAGE_DEPENDS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}) list(APPEND MODULES_PACKAGE_DEPENDS_DIRS ${MITK_PACKAGE_DEPENDS_EXTENSION_DIR}) endif() endforeach() if(NOT MITK_USE_SYSTEM_Boost) set(Boost_NO_SYSTEM_PATHS 1) endif() set(Boost_USE_MULTITHREADED 1) set(Boost_USE_STATIC_LIBS 0) set(Boost_USE_STATIC_RUNTIME 0) set(Boost_ADDITIONAL_VERSIONS 1.68 1.68.0) # We need this later for a DCMTK workaround set(_dcmtk_dir_orig ${DCMTK_DIR}) # This property is populated at the top half of this file get_property(MITK_EXTERNAL_PROJECTS GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(MITK_USE_${ep} AND _package) if(_components) find_package(${_package} COMPONENTS ${_components} REQUIRED CONFIG) else() # Prefer config mode first because it finds external # Config.cmake files pointed at by _DIR variables. # Otherwise, existing Find.cmake files could fail. if(DEFINED ${_package}_DIR) #we store the information because it will be overwritten by find_package #and would get lost for all EPs that use on Find.cmake instead of config #files. set(_temp_EP_${_package}_dir ${${_package}_DIR}) endif(DEFINED ${_package}_DIR) find_package(${_package} QUIET CONFIG) string(TOUPPER "${_package}" _package_uc) if(NOT (${_package}_FOUND OR ${_package_uc}_FOUND)) if(DEFINED _temp_EP_${_package}_dir) set(${_package}_DIR ${_temp_EP_${_package}_dir} CACHE PATH "externaly set dir of the package ${_package}" FORCE) endif(DEFINED _temp_EP_${_package}_dir) find_package(${_package} REQUIRED) endif() endif() endif() endforeach() # Ensure that the MITK CMake module path comes first set(CMAKE_MODULE_PATH ${MITK_CMAKE_DIR} ${CMAKE_MODULE_PATH} ) if(MITK_USE_DCMTK) # Due to the preferred CONFIG mode in find_package calls above, # the DCMTKConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMTK. if(${_dcmtk_dir_orig} MATCHES "${MITK_EXTERNAL_PROJECT_PREFIX}.*") # Help our FindDCMTK.cmake script find our super-build DCMTK set(DCMTK_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) else() # Use the original value set(DCMTK_DIR ${_dcmtk_dir_orig}) endif() find_package(DCMTK REQUIRED MODULE) endif() if(MITK_USE_DCMQI) # Due to the preferred CONFIG mode in find_package calls above, # the DCMQIConfig.cmake file is read, which does not provide useful # package information. We explictly need MODULE mode to find DCMQI. # Help our FindDCMQI.cmake script find our super-build DCMQI set(DCMQI_DIR ${MITK_EXTERNAL_PROJECT_PREFIX}) find_package(DCMQI REQUIRED) endif() link_directories(${Boost_LIBRARY_DIRS}) if(MITK_USE_OpenIGTLink) link_directories(${OpenIGTLink_LIBRARY_DIRS}) endif() if(MITK_USE_OpenCL) find_package(OpenCL REQUIRED) endif() if(MITK_USE_OpenMP) find_package(OpenMP REQUIRED COMPONENTS CXX) else() find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) elseif(APPLE AND OpenMP_libomp_LIBRARY AND NOT OpenMP_CXX_LIB_NAMES) set(OpenMP_CXX_LIB_NAMES libomp CACHE STRING "" FORCE) get_filename_component(openmp_lib_dir "${OpenMP_libomp_LIBRARY}" DIRECTORY) set(openmp_include_dir "${openmp_lib_dir}/../include") if(EXISTS "${openmp_include_dir}") get_filename_component(openmp_include_dir "${openmp_include_dir}" REALPATH) set(OpenMP_CXX_FLAGS "-Xpreprocessor -fopenmp -I${openmp_include_dir}" CACHE STRING "" FORCE) find_package(OpenMP QUIET COMPONENTS CXX) if(OpenMP_FOUND) set(MITK_USE_OpenMP ON CACHE BOOL "" FORCE) endif() endif() endif() endif() # Qt support if(MITK_USE_Qt5) find_package(Qt5Core ${MITK_QT5_MINIMUM_VERSION} REQUIRED) # at least Core required get_target_property(_qmake_exec Qt5::qmake LOCATION) execute_process(COMMAND ${_qmake_exec} -query QT_INSTALL_BINS RESULT_VARIABLE _result OUTPUT_VARIABLE QT_BINARY_DIR ERROR_VARIABLE _error ) string(STRIP "${QT_BINARY_DIR}" QT_BINARY_DIR) if(_result OR NOT EXISTS "${QT_BINARY_DIR}") message(FATAL_ERROR "Could not determine Qt binary directory: ${_result} ${QT_BINARY_DIR} ${_error}") endif() find_program(QT_HELPGENERATOR_EXECUTABLE NAMES qhelpgenerator qhelpgenerator-qt5 qhelpgenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_COLLECTIONGENERATOR_EXECUTABLE NAMES qcollectiongenerator qcollectiongenerator-qt5 qcollectiongenerator5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_ASSISTANT_EXECUTABLE NAMES assistant assistant-qt5 assistant5 PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) find_program(QT_XMLPATTERNS_EXECUTABLE NAMES xmlpatterns PATHS ${QT_BINARY_DIR} NO_DEFAULT_PATH ) mark_as_advanced(QT_HELPGENERATOR_EXECUTABLE QT_COLLECTIONGENERATOR_EXECUTABLE QT_ASSISTANT_EXECUTABLE QT_XMLPATTERNS_EXECUTABLE ) if(MITK_USE_BLUEBERRY) option(BLUEBERRY_USE_QT_HELP "Enable support for integrating plugin documentation into Qt Help" ${DOXYGEN_FOUND}) mark_as_advanced(BLUEBERRY_USE_QT_HELP) # Sanity checks for in-application BlueBerry plug-in help generation if(BLUEBERRY_USE_QT_HELP) set(_force_blueberry_use_qt_help_to_off 0) if(NOT DOXYGEN_FOUND) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen was not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(DOXYGEN_FOUND AND DOXYGEN_VERSION VERSION_LESS 1.8.7) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because Doxygen version 1.8.7 or newer not found.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_HELPGENERATOR_EXECUTABLE) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because QT_HELPGENERATOR_EXECUTABLE is empty.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT MITK_USE_Qt5) message("> Forcing BLUEBERRY_USE_QT_HELP to OFF because MITK_USE_Qt5 is OFF.") set(_force_blueberry_use_qt_help_to_off 1) endif() if(NOT QT_XMLPATTERNS_EXECUTABLE) message("You have enabled Qt Help support, but QT_XMLPATTERNS_EXECUTABLE is empty") set(_force_blueberry_use_qt_help_to_off 1) endif() if(_force_blueberry_use_qt_help_to_off) set(BLUEBERRY_USE_QT_HELP OFF CACHE BOOL "Enable support for integrating plugin documentation into Qt Help" FORCE) endif() endif() if(BLUEBERRY_QT_HELP_REQUIRED AND NOT BLUEBERRY_USE_QT_HELP) message(FATAL_ERROR "BLUEBERRY_USE_QT_HELP is required to be set to ON") endif() endif() endif() #----------------------------------------------------------------------------- # Testing #----------------------------------------------------------------------------- if(BUILD_TESTING) enable_testing() include(CTest) mark_as_advanced(TCL_TCLSH DART_ROOT) # Setup file for setting custom ctest vars configure_file( CMake/CTestCustom.cmake.in ${MITK_BINARY_DIR}/CTestCustom.cmake @ONLY ) # Initial cache for ProjectTemplate and PluginGenerator tests configure_file( CMake/mitkTestInitialCache.txt.in ${MITK_BINARY_DIR}/mitkTestInitialCache.txt @ONLY ) # Configuration for the CMake-generated test driver set(CMAKE_TESTDRIVER_EXTRA_INCLUDES "#include ") set(CMAKE_TESTDRIVER_BEFORE_TESTMAIN " try {") set(CMAKE_TESTDRIVER_AFTER_TESTMAIN " } catch ( const std::exception & excp ) { fprintf(stderr,\"%s\\n\",excp.what()); return EXIT_FAILURE; } catch( ... ) { printf(\"Exception caught in the test driver\\n\"); return EXIT_FAILURE; } ") set(MITK_TEST_OUTPUT_DIR "${MITK_BINARY_DIR}/test_output") if(NOT EXISTS ${MITK_TEST_OUTPUT_DIR}) file(MAKE_DIRECTORY ${MITK_TEST_OUTPUT_DIR}) endif() # Test the package target include(mitkPackageTest) endif() configure_file(mitkTestingConfig.h.in ${MITK_BINARY_DIR}/mitkTestingConfig.h) #----------------------------------------------------------------------------- # MITK_SUPERBUILD_BINARY_DIR #----------------------------------------------------------------------------- # If MITK_SUPERBUILD_BINARY_DIR isn't defined, it means MITK is *NOT* build using Superbuild. # In that specific case, MITK_SUPERBUILD_BINARY_DIR should default to MITK_BINARY_DIR if(NOT DEFINED MITK_SUPERBUILD_BINARY_DIR) set(MITK_SUPERBUILD_BINARY_DIR ${MITK_BINARY_DIR}) endif() #----------------------------------------------------------------------------- # Set C/CXX and linker flags for MITK code #----------------------------------------------------------------------------- set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${MITK_CXX_FLAGS}") set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} ${MITK_CXX_FLAGS_DEBUG}") set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} ${MITK_CXX_FLAGS_RELEASE}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${MITK_C_FLAGS}") set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} ${MITK_C_FLAGS_DEBUG}") set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} ${MITK_C_FLAGS_RELEASE}") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} ${MITK_EXE_LINKER_FLAGS}") set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} ${MITK_SHARED_LINKER_FLAGS}") set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} ${MITK_MODULE_LINKER_FLAGS}") #----------------------------------------------------------------------------- # Add custom targets representing CDash subprojects #----------------------------------------------------------------------------- foreach(subproject ${CTEST_PROJECT_SUBPROJECTS}) if(NOT TARGET ${subproject} AND NOT subproject MATCHES "Unlabeled") add_custom_target(${subproject}) set_property(TARGET ${subproject} PROPERTY FOLDER "${MITK_ROOT_FOLDER}/CTestSubprojects") endif() endforeach() #----------------------------------------------------------------------------- # Add subdirectories #----------------------------------------------------------------------------- add_subdirectory(Utilities) add_subdirectory(Modules) include("${CMAKE_CURRENT_SOURCE_DIR}/Modules/ModuleList.cmake") mitkFunctionWhitelistModules(MITK MITK_MODULES) set(MITK_ROOT_FOLDER_BACKUP "${MITK_ROOT_FOLDER}") foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) get_filename_component(MITK_ROOT_FOLDER ${MITK_EXTENSION_DIR} NAME) set(MITK_MODULES_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Modules) get_filename_component(MITK_MODULES_EXTENSION_DIR ${MITK_MODULES_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake) set(MITK_MODULES "") include(${MITK_MODULES_EXTENSION_DIR}/ModuleList.cmake) foreach(mitk_module ${MITK_MODULES}) add_subdirectory(${MITK_MODULES_EXTENSION_DIR}/${mitk_module} Modules/${mitk_module}) endforeach() endif() set(MITK_MODULE_NAME_PREFIX ${MITK_DEFAULT_MODULE_NAME_PREFIX}) endforeach() set(MITK_ROOT_FOLDER "${MITK_ROOT_FOLDER_BACKUP}") add_subdirectory(Wrapping) set(MITK_DOXYGEN_OUTPUT_DIR "${PROJECT_BINARY_DIR}/Documentation/Doxygen" CACHE PATH "Output directory for doxygen generated documentation.") if(MITK_USE_BLUEBERRY) set(BLUEBERRY_XPDOC_OUTPUT_DIR "${MITK_DOXYGEN_OUTPUT_DIR}/html/extension-points/html/") execute_process(COMMAND ${CMAKE_COMMAND} -E make_directory ${BLUEBERRY_XPDOC_OUTPUT_DIR}) include("${CMAKE_CURRENT_SOURCE_DIR}/Plugins/PluginList.cmake") mitkFunctionWhitelistPlugins(MITK MITK_PLUGINS) set(mitk_plugins_fullpath "") foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath Plugins/${mitk_plugin}) endforeach() set(MITK_PLUGIN_REGEX_LIST "") foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_PLUGINS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Plugins) get_filename_component(MITK_PLUGINS_EXTENSION_DIR ${MITK_PLUGINS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake) set(MITK_PLUGINS "") include(${MITK_PLUGINS_EXTENSION_DIR}/PluginList.cmake) foreach(mitk_plugin ${MITK_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PLUGINS_EXTENSION_DIR}/${mitk_plugin}) endforeach() endif() endforeach() if(EXISTS ${MITK_PRIVATE_MODULES}/PluginList.cmake) include(${MITK_PRIVATE_MODULES}/PluginList.cmake) foreach(mitk_plugin ${MITK_PRIVATE_PLUGINS}) list(APPEND mitk_plugins_fullpath ${MITK_PRIVATE_MODULES}/${mitk_plugin}) endforeach() endif() if(MITK_BUILD_EXAMPLES) include("${CMAKE_CURRENT_SOURCE_DIR}/Examples/Plugins/PluginList.cmake") set(mitk_example_plugins_fullpath ) foreach(mitk_example_plugin ${MITK_EXAMPLE_PLUGINS}) list(APPEND mitk_example_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) list(APPEND mitk_plugins_fullpath Examples/Plugins/${mitk_example_plugin}) endforeach() endif() # Specify which plug-ins belong to this project macro(GetMyTargetLibraries all_target_libraries varname) set(re_ctkplugin_mitk "^org_mitk_[a-zA-Z0-9_]+$") set(re_ctkplugin_bb "^org_blueberry_[a-zA-Z0-9_]+$") set(_tmp_list) list(APPEND _tmp_list ${all_target_libraries}) ctkMacroListFilter(_tmp_list re_ctkplugin_mitk re_ctkplugin_bb MITK_PLUGIN_REGEX_LIST OUTPUT_VARIABLE ${varname}) endmacro() # Get infos about application directories and build options set(mitk_apps_fullpath "") foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) set(MITK_APPS "") include(${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) foreach(mitk_app ${MITK_APPS}) # extract option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) if(${option_name}) list(APPEND mitk_apps_fullpath "${MITK_APPLICATIONS_EXTENSION_DIR}/${directory_name}^^${option_name}") endif() endforeach() endif() endforeach() if (mitk_plugins_fullpath) ctkMacroSetupPlugins(${mitk_plugins_fullpath} BUILD_OPTION_PREFIX MITK_BUILD_ APPS ${mitk_apps_fullpath} BUILD_ALL ${MITK_BUILD_ALL_PLUGINS} COMPACT_OPTIONS) endif() set(MITK_PLUGIN_USE_FILE "${MITK_BINARY_DIR}/MitkPluginUseFile.cmake") if(${PROJECT_NAME}_PLUGIN_LIBRARIES) ctkFunctionGeneratePluginUseFile(${MITK_PLUGIN_USE_FILE}) else() file(REMOVE ${MITK_PLUGIN_USE_FILE}) set(MITK_PLUGIN_USE_FILE ) endif() endif() #----------------------------------------------------------------------------- # Documentation #----------------------------------------------------------------------------- if(DOXYGEN_FOUND) add_subdirectory(Documentation) endif() #----------------------------------------------------------------------------- # Installation #----------------------------------------------------------------------------- # set MITK cpack variables # These are the default variables, which can be overwritten ( see below ) include(mitkSetupCPack) set(use_default_config ON) set(ALL_MITK_APPS "") set(activated_apps_no 0) foreach(MITK_EXTENSION_DIR ${MITK_DIR_PLUS_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) set(MITK_APPS "") include(${MITK_APPLICATIONS_EXTENSION_DIR}/AppList.cmake) foreach(mitk_app ${MITK_APPS}) string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 directory_name) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) list(APPEND ALL_MITK_APPS "${MITK_EXTENSION_DIR}/Applications/${directory_name}^^${option_name}^^${executable_name}") if(${option_name} OR MITK_BUILD_ALL_APPS) MATH(EXPR activated_apps_no "${activated_apps_no} + 1") endif() endforeach() endif() endforeach() list(LENGTH ALL_MITK_APPS app_count) if(app_count EQUAL 1 AND (activated_apps_no EQUAL 1 OR MITK_BUILD_ALL_APPS)) # Corner case if there is only one app in total set(use_project_cpack ON) elseif(activated_apps_no EQUAL 1 AND NOT MITK_BUILD_ALL_APPS) # Only one app is enabled (no "build all" flag set) set(use_project_cpack ON) else() # Less or more then one app is enabled set(use_project_cpack OFF) endif() foreach(mitk_app ${ALL_MITK_APPS}) # extract target_dir and option_name string(REPLACE "^^" "\\;" target_info ${mitk_app}) set(target_info_list ${target_info}) list(GET target_info_list 0 target_dir) list(GET target_info_list 1 option_name) list(GET target_info_list 2 executable_name) # check if the application is enabled if(${option_name} OR MITK_BUILD_ALL_APPS) # check whether application specific configuration files will be used if(use_project_cpack) # use files if they exist if(EXISTS "${target_dir}/CPackOptions.cmake") include("${target_dir}/CPackOptions.cmake") endif() if(EXISTS "${target_dir}/CPackConfig.cmake.in") set(CPACK_PROJECT_CONFIG_FILE "${target_dir}/CPackConfig.cmake") configure_file(${target_dir}/CPackConfig.cmake.in ${CPACK_PROJECT_CONFIG_FILE} @ONLY) set(use_default_config OFF) endif() endif() # add link to the list list(APPEND CPACK_CREATE_DESKTOP_LINKS "${executable_name}") endif() endforeach() # if no application specific configuration file was used, use default if(use_default_config) configure_file(${MITK_SOURCE_DIR}/MITKCPackOptions.cmake.in ${MITK_BINARY_DIR}/MITKCPackOptions.cmake @ONLY) set(CPACK_PROJECT_CONFIG_FILE "${MITK_BINARY_DIR}/MITKCPackOptions.cmake") endif() # include CPack model once all variables are set include(CPack) # Additional installation rules include(mitkInstallRules) #----------------------------------------------------------------------------- # Last configuration steps #----------------------------------------------------------------------------- # ---------------- Export targets ----------------- set(MITK_EXPORTS_FILE "${MITK_BINARY_DIR}/MitkExports.cmake") file(REMOVE ${MITK_EXPORTS_FILE}) set(targets_to_export) get_property(module_targets GLOBAL PROPERTY MITK_MODULE_TARGETS) if(module_targets) list(APPEND targets_to_export ${module_targets}) endif() if(MITK_USE_BLUEBERRY) if(MITK_PLUGIN_LIBRARIES) list(APPEND targets_to_export ${MITK_PLUGIN_LIBRARIES}) endif() endif() export(TARGETS ${targets_to_export} APPEND FILE ${MITK_EXPORTS_FILE}) set(MITK_EXPORTED_TARGET_PROPERTIES ) foreach(target_to_export ${targets_to_export}) get_target_property(autoload_targets ${target_to_export} MITK_AUTOLOAD_TARGETS) if(autoload_targets) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_TARGETS \"${autoload_targets}\")") endif() get_target_property(autoload_dir ${target_to_export} MITK_AUTOLOAD_DIRECTORY) if(autoload_dir) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_AUTOLOAD_DIRECTORY \"${autoload_dir}\")") endif() get_target_property(deprecated_module ${target_to_export} MITK_MODULE_DEPRECATED_SINCE) if(deprecated_module) set(MITK_EXPORTED_TARGET_PROPERTIES "${MITK_EXPORTED_TARGET_PROPERTIES} set_target_properties(${target_to_export} PROPERTIES MITK_MODULE_DEPRECATED_SINCE \"${deprecated_module}\")") endif() endforeach() # ---------------- External projects ----------------- get_property(MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS_CONFIG GLOBAL PROPERTY MITK_ADDITIONAL_LIBRARY_SEARCH_PATHS) set(MITK_CONFIG_EXTERNAL_PROJECTS ) #string(REPLACE "^^" ";" _mitk_external_projects ${MITK_EXTERNAL_PROJECTS}) foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} set(MITK_USE_${ep} ${MITK_USE_${ep}}) set(MITK_${ep}_DIR \"${${ep}_DIR}\") set(MITK_${ep}_COMPONENTS ${_components}) ") endforeach() foreach(ep ${MITK_EXTERNAL_PROJECTS}) get_property(_package GLOBAL PROPERTY MITK_${ep}_PACKAGE) get_property(_components GLOBAL PROPERTY MITK_${ep}_COMPONENTS) if(_components) set(_components_arg COMPONENTS \${_components}) else() set(_components_arg) endif() if(_package) set(MITK_CONFIG_EXTERNAL_PROJECTS "${MITK_CONFIG_EXTERNAL_PROJECTS} if(MITK_USE_${ep}) set(${ep}_DIR \${MITK_${ep}_DIR}) if(MITK_${ep}_COMPONENTS) mitkMacroFindDependency(${_package} COMPONENTS \${MITK_${ep}_COMPONENTS}) else() mitkMacroFindDependency(${_package}) endif() endif()") endif() endforeach() # ---------------- Tools ----------------- configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactory.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolExtensionITKFactoryLoader.cpp.in ${MITK_BINARY_DIR}/ToolExtensionITKFactoryLoader.cpp.in COPYONLY) configure_file(${MITK_SOURCE_DIR}/CMake/ToolGUIExtensionITKFactory.cpp.in ${MITK_BINARY_DIR}/ToolGUIExtensionITKFactory.cpp.in COPYONLY) # ---------------- Configure files ----------------- configure_file(mitkVersion.h.in ${MITK_BINARY_DIR}/mitkVersion.h) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) set(IPFUNC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities/ipFunc) set(UTILITIES_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Utilities) configure_file(mitkConfig.h.in ${MITK_BINARY_DIR}/mitkConfig.h) configure_file(MITKConfig.cmake.in ${MITK_BINARY_DIR}/MITKConfig.cmake @ONLY) write_basic_config_version_file(${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake VERSION ${MITK_VERSION_STRING} COMPATIBILITY AnyNewerVersion) #----------------------------------------------------------------------------- # MITK Applications #----------------------------------------------------------------------------- # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Applications) if(MSVC AND TARGET MitkWorkbench) set_directory_properties(PROPERTIES VS_STARTUP_PROJECT MitkWorkbench) endif() foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) set(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/Applications) get_filename_component(MITK_APPLICATIONS_EXTENSION_DIR ${MITK_APPLICATIONS_EXTENSION_DIR} ABSOLUTE) if(EXISTS ${MITK_APPLICATIONS_EXTENSION_DIR}/CMakeLists.txt) add_subdirectory(${MITK_APPLICATIONS_EXTENSION_DIR} Applications) endif() endforeach() #----------------------------------------------------------------------------- # MITK Examples #----------------------------------------------------------------------------- if(MITK_BUILD_EXAMPLES) # This must come after MITKConfig.h was generated, since applications # might do a find_package(MITK REQUIRED). add_subdirectory(Examples) endif() #----------------------------------------------------------------------------- # Print configuration summary #----------------------------------------------------------------------------- message("\n\n") feature_summary( DESCRIPTION "------- FEATURE SUMMARY FOR ${PROJECT_NAME} -------" WHAT ALL ) diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.cpp b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.cpp index 0fd2a438da..1bfdb684f6 100644 --- a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.cpp +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.cpp @@ -1,91 +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. ============================================================================*/ #include "mitkExampleDataStructureReaderService.h" // mitk includes #include "mitkExampleIOMimeTypes.h" #include "mitkGeometry3D.h" #include // itk includes #include "itksys/SystemTools.hxx" namespace mitk { ExampleDataStructureReaderService::ExampleDataStructureReaderService(const ExampleDataStructureReaderService &other) : mitk::AbstractFileReader(other) { } ExampleDataStructureReaderService::ExampleDataStructureReaderService() : mitk::AbstractFileReader(CustomMimeType(mitk::ExampleIOMimeTypes::EXAMPLE_MIMETYPE()), "Default reader for the example data structure") { m_ServiceReg = this->RegisterService(); } ExampleDataStructureReaderService::~ExampleDataStructureReaderService() {} - std::vector> ExampleDataStructureReaderService::Read() + std::vector> ExampleDataStructureReaderService::DoRead() { std::vector> result; std::string location = GetInputLocation(); std::string ext = itksys::SystemTools::GetFilenameLastExtension(location); ext = itksys::SystemTools::LowerCase(ext); if (location == "") { MITK_ERROR << "No file name specified."; } try { std::ifstream file(location); std::string content(""); std::string line(""); if (file.is_open()) { while (getline(file, line)) { content += line; content += "\n"; } } else { mitkThrow() << "Could not open file " << this->GetInputLocation() << " for reading."; } mitk::ExampleDataStructure::Pointer outputData = mitk::ExampleDataStructure::New(); outputData->SetData(content); result.push_back(outputData.GetPointer()); MITK_INFO << "Example file read"; } catch (const mitk::Exception& e) { MITK_ERROR << e.GetDescription(); } catch (...) { MITK_ERROR << "Unknown error occurred while trying to read file."; } return result; } } // namespace MITK mitk::ExampleDataStructureReaderService *mitk::ExampleDataStructureReaderService::Clone() const { return new ExampleDataStructureReaderService(*this); } diff --git a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.h b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.h index 4f1384ed8e..bdc2ab20e8 100644 --- a/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.h +++ b/Examples/FirstSteps/NewModule/autoload/IO/mitkExampleDataStructureReaderService.h @@ -1,47 +1,49 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 __mitkExampleDataStructureReaderService_h #define __mitkExampleDataStructureReaderService_h #include "mitkCommon.h" #include "mitkExampleDataStructure.h" #include #include #include namespace mitk { /** \brief The reader service for the MITK example data type */ class ExampleDataStructureReaderService : public mitk::AbstractFileReader { public: typedef mitk::ExampleDataStructure OutputType; ExampleDataStructureReaderService(const ExampleDataStructureReaderService &other); ExampleDataStructureReaderService(); ~ExampleDataStructureReaderService() override; using AbstractFileReader::Read; - std::vector> Read() override; + + protected: + std::vector> DoRead() override; private: ExampleDataStructureReaderService *Clone() const override; us::ServiceRegistration m_ServiceReg; }; } // namespace MITK #endif // __mitkExampleDataStructureReaderService_h diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkDummyLsetReader.h b/Modules/Classification/CLVigraRandomForest/include/mitkDummyLsetReader.h index b840cf141d..f118964448 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkDummyLsetReader.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkDummyLsetReader.h @@ -1,45 +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 _MITK_DummyLsetFileReader__H_ #define _MITK_DummyLsetFileReader__H_ #include namespace mitk { /** * Read deprecated *.lset format (Multilabel Image format 2014) * @ingroup Process */ class DummyLsetFileReader : public mitk::AbstractFileReader { public: DummyLsetFileReader(); DummyLsetFileReader(const mitk::DummyLsetFileReader& other); ~DummyLsetFileReader() override; using AbstractFileReader::Read; - std::vector > Read() override; + +protected: + std::vector> DoRead() override; + private: DummyLsetFileReader * Clone() const override; }; } // end of namespace mitk #endif diff --git a/Modules/Classification/CLVigraRandomForest/include/mitkRandomForestIO.h b/Modules/Classification/CLVigraRandomForest/include/mitkRandomForestIO.h index 1bcb652b7f..456837251c 100644 --- a/Modules/Classification/CLVigraRandomForest/include/mitkRandomForestIO.h +++ b/Modules/Classification/CLVigraRandomForest/include/mitkRandomForestIO.h @@ -1,61 +1,62 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_DecisionForestFileIO__H_ #define _MITK_DecisionForestFileIO__H_ #include #include "vigra/random_forest.hxx" namespace mitk { /** * Writes vigra based mitk::DecisionForest * @ingroup Process */ class RandomForestFileIO : public mitk::AbstractFileIO { public: RandomForestFileIO(); RandomForestFileIO(const mitk::RandomForestFileIO& other); ~RandomForestFileIO() override; using AbstractFileIO::Write; void Write() override; using AbstractFileIO::Read; - std::vector > Read() override; ConfidenceLevel GetReaderConfidenceLevel() const override; ConfidenceLevel GetWriterConfidenceLevel() const override; - protected: +protected: + std::vector> DoRead() override; + mutable vigra::RandomForest m_rf; // DecisionForestFileIO(const DecisionForestFileIO& other); // virtual mitk::DecisionForestFileIO* Clone() const; private: AbstractFileIO* IOClone() const override; }; } // end of namespace mitk #endif diff --git a/Modules/Classification/CLVigraRandomForest/src/IO/mitkDummyLsetReader.cpp b/Modules/Classification/CLVigraRandomForest/src/IO/mitkDummyLsetReader.cpp index a4e6256ba0..a1ae48a41a 100644 --- a/Modules/Classification/CLVigraRandomForest/src/IO/mitkDummyLsetReader.cpp +++ b/Modules/Classification/CLVigraRandomForest/src/IO/mitkDummyLsetReader.cpp @@ -1,66 +1,66 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include typedef itk::Image ImageType; -std::vector > mitk::DummyLsetFileReader::Read() +std::vector > mitk::DummyLsetFileReader::DoRead() { std::vector > result; typedef itk::ImageFileReader FileReaderType; FileReaderType::Pointer reader = FileReaderType::New(); reader->SetFileName(this->GetInputLocation()); itk::NrrdImageIO::Pointer io = itk::NrrdImageIO::New(); reader->SetImageIO(io); reader->Update(); mitk::Image::Pointer img; mitk::CastToMitkImage(reader->GetOutput(),img); result.push_back(img.GetPointer()); return result; } mitk::DummyLsetFileReader::DummyLsetFileReader(const DummyLsetFileReader & other) : AbstractFileReader(other) { } mitk::DummyLsetFileReader* mitk::DummyLsetFileReader::Clone() const { return new DummyLsetFileReader(*this); } mitk::DummyLsetFileReader::~DummyLsetFileReader() {} mitk::DummyLsetFileReader::DummyLsetFileReader() { CustomMimeType mimeType(this->GetMimeTypePrefix() + "lset"); mimeType.AddExtension("lset"); mimeType.SetCategory("Images"); mimeType.SetComment("Experimental MBI LabelSetImage"); this->SetMimeType(mimeType); this->SetDescription("MBI LabelSetImage"); this->RegisterService(); } diff --git a/Modules/Classification/CLVigraRandomForest/src/IO/mitkRandomForestIO.cpp b/Modules/Classification/CLVigraRandomForest/src/IO/mitkRandomForestIO.cpp index 4dedf92d42..79a0e270fb 100644 --- a/Modules/Classification/CLVigraRandomForest/src/IO/mitkRandomForestIO.cpp +++ b/Modules/Classification/CLVigraRandomForest/src/IO/mitkRandomForestIO.cpp @@ -1,218 +1,218 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 __mitkDecisionForestIO__cpp #define __mitkDecisionForestIO__cpp #include "mitkRandomForestIO.h" #include "itksys/SystemTools.hxx" //#include "mitkHDF5IOMimeTypes.h" #include "vigra/random_forest_hdf5_impex.hxx" #include #include #include "mitkVigraRandomForestClassifier.h" #include "mitkIOMimeTypes.h" #define GetAttribute(name,type)\ type name;\ hdf5_file.readAttribute(".",name,name); mitk::RandomForestFileIO::ConfidenceLevel mitk::RandomForestFileIO::GetReaderConfidenceLevel() const { std::string ext = itksys::SystemTools::GetFilenameLastExtension(this->GetLocalFileName().c_str()); bool is_loaded = vigra::rf_import_HDF5(m_rf, this->GetInputLocation()); return ext == ".forest" && is_loaded == true? IFileReader::Supported : IFileReader::Unsupported; } mitk::RandomForestFileIO::ConfidenceLevel mitk::RandomForestFileIO::GetWriterConfidenceLevel() const { mitk::VigraRandomForestClassifier::ConstPointer input = dynamic_cast(this->GetInput()); if (input.IsNull()) { return IFileWriter::Unsupported; }else{ return IFileWriter::Supported; } } mitk::RandomForestFileIO::RandomForestFileIO() : AbstractFileIO(mitk::VigraRandomForestClassifier::GetStaticNameOfClass()) { CustomMimeType customReaderMimeType(mitk::IOMimeTypes::DEFAULT_BASE_NAME() + ".forest"); std::string category = "Vigra Random Forest File"; customReaderMimeType.SetComment("Vigra Random Forest"); customReaderMimeType.SetCategory(category); customReaderMimeType.AddExtension("forest"); // this->AbstractFileIOWriter::SetRanking(100); this->AbstractFileWriter::SetMimeTypePrefix(mitk::IOMimeTypes::DEFAULT_BASE_NAME() + ".forest"); this->AbstractFileWriter::SetMimeType(customReaderMimeType); this->SetWriterDescription("Vigra Random Forest"); this->AbstractFileReader::SetMimeTypePrefix(mitk::IOMimeTypes::DEFAULT_BASE_NAME() + ".forest"); this->AbstractFileReader::SetMimeType(customReaderMimeType); this->SetReaderDescription("Vigra Random Forest"); // this->SetReaderDescription(mitk::DecisionForestIOMimeTypes::DECISIONFOREST_MIMETYPE_DESCRIPTION()); // this->SetWriterDescription(mitk::DecisionForestIOMimeTypes::DECISIONFOREST_MIMETYPE_DESCRIPTION()); this->RegisterService(); } mitk::RandomForestFileIO::RandomForestFileIO(const mitk::RandomForestFileIO& other) : AbstractFileIO(other) { } mitk::RandomForestFileIO::~RandomForestFileIO() {} std::vector > mitk::RandomForestFileIO:: -Read() +DoRead() { mitk::VigraRandomForestClassifier::Pointer output = mitk::VigraRandomForestClassifier::New(); std::vector > result; if ( this->GetInputLocation().empty()) { MITK_ERROR << "Sorry, filename has not been set!"; return result; } else { const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, nullptr ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } output->SetRandomForest(m_rf); result.push_back(output.GetPointer()); vigra::HDF5File hdf5_file(this->GetInputLocation() , vigra::HDF5File::Open); hdf5_file.cd_mk("/_mitkOptions"); // --------------------------------------------------------- // Read tree weights if(hdf5_file.existsDataset("treeWeights")) { auto treeWeight = output->GetTreeWeights(); treeWeight.resize(m_rf.tree_count(),1); vigra::MultiArrayView<2, double> W(vigra::Shape2(treeWeight.rows(),treeWeight.cols()),treeWeight.data()); hdf5_file.read("treeWeights",W); output->SetTreeWeights(treeWeight); } // --------------------------------------------------------- // --------------------------------------------------------- // Read itemList if(hdf5_file.existsDataset("itemList")){ std::string items_string; hdf5_file.read("itemList",items_string); auto itemlist = output->GetItemList(); std::string current_item = ""; for(auto character : items_string) { if(character == ';'){ // skip seperator and push back item itemlist.push_back(current_item); current_item.clear(); }else{ current_item = current_item + character; } } output->SetItemList(itemlist); } // --------------------------------------------------------- hdf5_file.close(); return result; } } void mitk::RandomForestFileIO::Write() { mitk::BaseData::ConstPointer input = this->GetInput(); if (input.IsNull()) { MITK_ERROR <<"Sorry, input to NrrdDiffusionImageWriter is nullptr!"; return; } if ( this->GetOutputLocation().empty() ) { MITK_ERROR << "Sorry, filename has not been set!"; return ; }else{ const std::string& locale = "C"; const std::string& currLocale = setlocale( LC_ALL, nullptr ); if ( locale.compare(currLocale)!=0 ) { try { setlocale(LC_ALL, locale.c_str()); } catch(...) { MITK_INFO << "Could not set locale " << locale; } } mitk::VigraRandomForestClassifier::ConstPointer mitkDC = dynamic_cast(input.GetPointer()); //mitkDC->GetRandomForest() vigra::rf_export_HDF5(mitkDC->GetRandomForest(), this->GetOutputLocation()); vigra::HDF5File hdf5_file(this->GetOutputLocation() , vigra::HDF5File::Open); hdf5_file.cd_mk("/_mitkOptions"); // Write tree weights // --------------------------------------------------------- auto treeWeight = mitkDC->GetTreeWeights(); vigra::MultiArrayView<2, double> W(vigra::Shape2(treeWeight.rows(),treeWeight.cols()),treeWeight.data()); hdf5_file.write("treeWeights",W); // --------------------------------------------------------- // Write itemList // --------------------------------------------------------- auto items = mitkDC->GetItemList(); std::string item_stringlist; for(auto entry : items) item_stringlist = item_stringlist + entry + ";"; hdf5_file.write("itemList",item_stringlist); // --------------------------------------------------------- hdf5_file.close(); } } mitk::AbstractFileIO* mitk::RandomForestFileIO::IOClone() const { return new RandomForestFileIO(*this); } #endif diff --git a/Modules/ContourModel/IO/mitkContourModelReader.cpp b/Modules/ContourModel/IO/mitkContourModelReader.cpp index a7864602f2..fa0ead2c5f 100644 --- a/Modules/ContourModel/IO/mitkContourModelReader.cpp +++ b/Modules/ContourModel/IO/mitkContourModelReader.cpp @@ -1,157 +1,157 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkContourModelReader.h" #include #include #include #include mitk::ContourModelReader::ContourModelReader(const mitk::ContourModelReader &other) : mitk::AbstractFileReader(other) { } mitk::ContourModelReader::ContourModelReader() : AbstractFileReader() { std::string category = "Contour File"; mitk::CustomMimeType customMimeType; customMimeType.SetCategory(category); customMimeType.AddExtension("cnt"); this->SetDescription(category); this->SetMimeType(customMimeType); m_ServiceReg = this->RegisterService(); } mitk::ContourModelReader::~ContourModelReader() { } -std::vector> mitk::ContourModelReader::Read() +std::vector> mitk::ContourModelReader::DoRead() { std::vector> result; std::string location = GetInputLocation(); // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); try { TiXmlDocument doc(location.c_str()); bool loadOkay = doc.LoadFile(); if (loadOkay) { TiXmlHandle docHandle(&doc); /*++++ handle n contourModels within data tags ++++*/ for (TiXmlElement *currentContourElement = docHandle.FirstChildElement("contourModel").ToElement(); currentContourElement != nullptr; currentContourElement = currentContourElement->NextSiblingElement()) { mitk::ContourModel::Pointer newContourModel = mitk::ContourModel::New(); if (currentContourElement->FirstChildElement("data")->FirstChildElement("timestep") != nullptr) { // handle geometry information // TiXmlElement* currentGeometryInfo = // currentContourElement->FirstChildElement("head")->FirstChildElement("geometryInformation")->ToElement(); ///////////// NOT SUPPORTED YET //////////////// /*++++ handle n timesteps within timestep tags ++++*/ for (TiXmlElement *currentTimeSeries = currentContourElement->FirstChildElement("data")->FirstChildElement("timestep")->ToElement(); currentTimeSeries != nullptr; currentTimeSeries = currentTimeSeries->NextSiblingElement()) { unsigned int currentTimeStep(0); currentTimeStep = atoi(currentTimeSeries->Attribute("n")); this->ReadPoints(newContourModel, currentTimeSeries, currentTimeStep); int isClosed; currentTimeSeries->QueryIntAttribute("isClosed", &isClosed); if (isClosed) { newContourModel->Close(currentTimeStep); } } /*++++ END handle n timesteps within timestep tags ++++*/ } else { // this should not happen MITK_WARN << "wrong file format!"; // newContourModel = this->ReadPoint(newContourModel, currentContourElement, 0); } newContourModel->UpdateOutputInformation(); result.push_back(dynamic_cast(newContourModel.GetPointer())); } /*++++ END handle n contourModels within data tags ++++*/ } else { MITK_WARN << "XML parser error!"; } } catch (...) { MITK_ERROR << "Cannot read contourModel."; } return result; } mitk::ContourModelReader *mitk::ContourModelReader::Clone() const { return new ContourModelReader(*this); } void mitk::ContourModelReader::ReadPoints(mitk::ContourModel::Pointer newContourModel, TiXmlElement *currentTimeSeries, unsigned int currentTimeStep) { // check if the timesteps in contourModel have to be expanded if (currentTimeStep != newContourModel->GetTimeSteps()) { newContourModel->Expand(currentTimeStep + 1); } // read all points within controlPoints tag if (currentTimeSeries->FirstChildElement("controlPoints")->FirstChildElement("point") != nullptr) { for (TiXmlElement *currentPoint = currentTimeSeries->FirstChildElement("controlPoints")->FirstChildElement("point")->ToElement(); currentPoint != nullptr; currentPoint = currentPoint->NextSiblingElement()) { double x(0.0); double y(0.0); double z(0.0); x = atof(currentPoint->FirstChildElement("x")->GetText()); y = atof(currentPoint->FirstChildElement("y")->GetText()); z = atof(currentPoint->FirstChildElement("z")->GetText()); int isActivePoint; currentPoint->QueryIntAttribute("isActive", &isActivePoint); mitk::Point3D point; mitk::FillVector3D(point, x, y, z); newContourModel->AddVertex(point, isActivePoint, currentTimeStep); } } else { // nothing to read } } diff --git a/Modules/ContourModel/IO/mitkContourModelReader.h b/Modules/ContourModel/IO/mitkContourModelReader.h index d84bfdc3aa..bbeae0a7ec 100644 --- a/Modules/ContourModel/IO/mitkContourModelReader.h +++ b/Modules/ContourModel/IO/mitkContourModelReader.h @@ -1,56 +1,56 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_CONTOURMODEL_READER__H_ #define _MITK_CONTOURMODEL_READER__H_ // MITK #include #include #include #include #include #include #include #include namespace mitk { /** * @brief * @ingroup MitkContourModelModule */ class ContourModelReader : public mitk::AbstractFileReader { public: ContourModelReader(const ContourModelReader &other); ContourModelReader(); ~ContourModelReader() override; using AbstractFileReader::Read; - std::vector> Read() override; protected: virtual void ReadPoints(mitk::ContourModel::Pointer newContourModel, TiXmlElement *currentTimeSeries, unsigned int currentTimeStep); + std::vector> DoRead() override; private: ContourModelReader *Clone() const override; us::ServiceRegistration m_ServiceReg; }; } #endif diff --git a/Modules/ContourModel/IO/mitkContourModelSetReader.cpp b/Modules/ContourModel/IO/mitkContourModelSetReader.cpp index 615a802fc1..9af5f8f49b 100644 --- a/Modules/ContourModel/IO/mitkContourModelSetReader.cpp +++ b/Modules/ContourModel/IO/mitkContourModelSetReader.cpp @@ -1,77 +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 "mitkContourModelSetReader.h" #include "mitkContourModelReader.h" #include #include #include #include mitk::ContourModelSetReader::ContourModelSetReader(const mitk::ContourModelSetReader &other) : mitk::AbstractFileReader(other) { } mitk::ContourModelSetReader::ContourModelSetReader() : AbstractFileReader() { std::string category = "ContourModelSet File"; CustomMimeType customMimeType; customMimeType.SetCategory(category); customMimeType.AddExtension("cnt_set"); this->SetDescription(category); this->SetMimeType(customMimeType); m_ServiceReg = this->RegisterService(); } mitk::ContourModelSetReader::~ContourModelSetReader() { } -std::vector> mitk::ContourModelSetReader::Read() +std::vector> mitk::ContourModelSetReader::DoRead() { std::vector> result; std::vector> internalResult; std::string location = GetInputLocation(); // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); try { mitk::ContourModelSet::Pointer contourSet = mitk::ContourModelSet::New(); mitk::ContourModelReader reader; reader.SetInput(location); internalResult = reader.Read(); for (unsigned int i = 0; i < internalResult.size(); ++i) { contourSet->AddContourModel(dynamic_cast(internalResult.at(i).GetPointer())); } result.push_back(dynamic_cast(contourSet.GetPointer())); } catch (...) { MITK_ERROR << "Cannot read contourModel."; } return result; } mitk::ContourModelSetReader *mitk::ContourModelSetReader::Clone() const { return new ContourModelSetReader(*this); } diff --git a/Modules/ContourModel/IO/mitkContourModelSetReader.h b/Modules/ContourModel/IO/mitkContourModelSetReader.h index 1434ab76ea..91e24180a1 100644 --- a/Modules/ContourModel/IO/mitkContourModelSetReader.h +++ b/Modules/ContourModel/IO/mitkContourModelSetReader.h @@ -1,53 +1,54 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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_ContourModelSetReader__H_ #define _MITK_ContourModelSetReader__H_ // MITK #include #include #include #include #include #include #include #include #include namespace mitk { /** * @brief * @ingroup MitkContourModelModule */ class ContourModelSetReader : public mitk::AbstractFileReader { public: ContourModelSetReader(const ContourModelSetReader &other); ContourModelSetReader(); ~ContourModelSetReader() override; using AbstractFileReader::Read; - std::vector> Read() override; protected: + std::vector> DoRead() override; + private: ContourModelSetReader *Clone() const override; us::ServiceRegistration m_ServiceReg; }; } #endif diff --git a/Modules/Core/TestingHelper/src/mitkTestDynamicImageGenerator.cpp b/Modules/Core/TestingHelper/src/mitkTestDynamicImageGenerator.cpp index 73a8af779d..bca01c2eeb 100644 --- a/Modules/Core/TestingHelper/src/mitkTestDynamicImageGenerator.cpp +++ b/Modules/Core/TestingHelper/src/mitkTestDynamicImageGenerator.cpp @@ -1,253 +1,212 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTestDynamicImageGenerator.h" #include "mitkArbitraryTimeGeometry.h" #include "mitkImageCast.h" +#include "mitkTemporalJoinImagesFilter.h" namespace mitk { typedef itk::Image FrameITKImageType; typedef itk::Image DynamicITKImageType; typedef itk::Image MaskITKImageType; TestImageType::Pointer GenerateTestImage(int factor) { TestImageType::Pointer image = TestImageType::New(); TestImageType::IndexType start; start[0] = 0; // first index on X start[1] = 0; // first index on Y TestImageType::SizeType size; size[0] = 3; // size along X size[1] = 3; // size along Y TestImageType::RegionType region; region.SetSize(size); region.SetIndex(start); image->SetRegions(region); image->Allocate(); itk::ImageRegionIterator it = itk::ImageRegionIterator(image, image->GetLargestPossibleRegion()); int count = 1; while (!it.IsAtEnd()) { it.Set(count * factor); ++it; ++count; } return image; } TestMaskType::Pointer GenerateTestMask() { TestMaskType::Pointer image = TestMaskType::New(); TestMaskType::IndexType start; start[0] = 0; // first index on X start[1] = 0; // first index on Y TestMaskType::SizeType size; size[0] = 3; // size along X size[1] = 3; // size along Y TestMaskType::RegionType region; region.SetSize(size); region.SetIndex(start); image->SetRegions(region); image->Allocate(); itk::ImageRegionIterator it = itk::ImageRegionIterator(image, image->GetLargestPossibleRegion()); int count = 1; while (!it.IsAtEnd()) { if (count > 1 && count < 5) { it.Set(1); } else { it.Set(0); } ++it; ++count; } return image; } Image::Pointer GenerateTestFrame(double timePoint) { FrameITKImageType::Pointer image = FrameITKImageType::New(); FrameITKImageType::IndexType start; start[0] = 0; // first index on X start[1] = 0; // first index on Y start[2] = 0; // first index on Z FrameITKImageType::SizeType size; size[0] = 3; // size along X size[1] = 3; // size along Y size[2] = 3; // size along Z FrameITKImageType::RegionType region; region.SetSize(size); region.SetIndex(start); image->SetRegions(region); image->Allocate(); itk::ImageRegionIterator it = itk::ImageRegionIterator(image, image->GetLargestPossibleRegion()); int count = 0; while (!it.IsAtEnd()) { double slope = count % (size[0] * size[1]); double offset = itk::Math::Floor(count / (size[0] * size[1])) * 10; it.Set(slope * timePoint + offset); ++it; ++count; } mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk(image.GetPointer()); mitkImage->SetVolume(image->GetBufferPointer()); return mitkImage; } Image::Pointer GenerateTestMaskMITK() { MaskITKImageType::Pointer image = MaskITKImageType::New(); MaskITKImageType::IndexType start; start[0] = 0; // first index on X start[1] = 0; // first index on Y start[2] = 0; // first index on Z MaskITKImageType::SizeType size; size[0] = 3; // size along X size[1] = 3; // size along Y size[2] = 3; // size along Z MaskITKImageType::RegionType region; region.SetSize(size); region.SetIndex(start); image->SetRegions(region); image->Allocate(); itk::ImageRegionIterator it = itk::ImageRegionIterator(image, image->GetLargestPossibleRegion()); int count = 0; while (!it.IsAtEnd()) { if (count < 14) { it.Set(1); } else { it.Set(0); } ++it; ++count; } mitk::Image::Pointer mitkImage = mitk::Image::New(); mitkImage->InitializeByItk(image.GetPointer()); mitkImage->SetVolume(image->GetBufferPointer()); return mitkImage; } Image::Pointer GenerateDynamicTestImageMITK() { + auto filter = mitk::TemporalJoinImagesFilter::New(); - mitk::Image::Pointer tempImage = GenerateTestFrame(1); - mitk::Image::Pointer dynamicImage = mitk::Image::New(); - - DynamicITKImageType::Pointer dynamicITKImage = DynamicITKImageType::New(); - DynamicITKImageType::RegionType dynamicITKRegion; - DynamicITKImageType::PointType dynamicITKOrigin; - DynamicITKImageType::IndexType dynamicITKIndex; - DynamicITKImageType::SpacingType dynamicITKSpacing; - - dynamicITKSpacing[0] = tempImage->GetGeometry()->GetSpacing()[0]; - dynamicITKSpacing[1] = tempImage->GetGeometry()->GetSpacing()[1]; - dynamicITKSpacing[2] = tempImage->GetGeometry()->GetSpacing()[2]; - dynamicITKSpacing[3] = 5.0; - - dynamicITKIndex[0] = 0; // The first pixel of the REGION - dynamicITKIndex[1] = 0; - dynamicITKIndex[2] = 0; - dynamicITKIndex[3] = 0; - - dynamicITKRegion.SetSize(0, tempImage->GetDimension(0)); - dynamicITKRegion.SetSize(1, tempImage->GetDimension(1)); - dynamicITKRegion.SetSize(2, tempImage->GetDimension(2)); - dynamicITKRegion.SetSize(3, 10); - - dynamicITKRegion.SetIndex(dynamicITKIndex); - - dynamicITKOrigin[0] = tempImage->GetGeometry()->GetOrigin()[0]; - dynamicITKOrigin[1] = tempImage->GetGeometry()->GetOrigin()[1]; - dynamicITKOrigin[2] = tempImage->GetGeometry()->GetOrigin()[2]; - - dynamicITKImage->SetOrigin(dynamicITKOrigin); - dynamicITKImage->SetSpacing(dynamicITKSpacing); - dynamicITKImage->SetRegions(dynamicITKRegion); - dynamicITKImage->Allocate(); - dynamicITKImage->FillBuffer(0); //not sure if this is necessary - - // Convert - mitk::CastToMitkImage(dynamicITKImage, dynamicImage); - - ArbitraryTimeGeometry::Pointer timeGeometry = ArbitraryTimeGeometry::New(); - timeGeometry->ClearAllGeometries(); - - + mitk::TemporalJoinImagesFilter::TimeBoundsVectorType bounds; for (int i = 0; i < 10; ++i) { - mitk::Image::Pointer frameImage = GenerateTestFrame(1 + (dynamicITKSpacing[3] * i)); - mitk::ImageReadAccessor accessor(frameImage); - dynamicImage->SetVolume(accessor.GetData(), i); - - timeGeometry->AppendNewTimeStepClone(frameImage->GetGeometry(), 1 + (dynamicITKSpacing[3] * i), - 1 + (dynamicITKSpacing[3]*(i+1))); + filter->SetInput(i, GenerateTestFrame(1 + (5.0 * i))); + bounds.push_back(1 + (5.0 * (i + 1))); } - dynamicImage->SetTimeGeometry(timeGeometry); + filter->SetFirstMinTimeBound(1.); + filter->SetMaxTimeBounds(bounds); + + filter->Update(); - return dynamicImage; + return filter->GetOutput(); } } diff --git a/Modules/Core/files.cmake b/Modules/Core/files.cmake index e13405173c..75407c5ac8 100644 --- a/Modules/Core/files.cmake +++ b/Modules/Core/files.cmake @@ -1,323 +1,325 @@ 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/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/mitkAbstractFileReader.h b/Modules/Core/include/mitkAbstractFileReader.h index 8b21bbde6b..83ab25d7f3 100644 --- a/Modules/Core/include/mitkAbstractFileReader.h +++ b/Modules/Core/include/mitkAbstractFileReader.h @@ -1,232 +1,241 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 AbstractFileReader_H_HEADER_INCLUDED_C1E7E521 #define AbstractFileReader_H_HEADER_INCLUDED_C1E7E521 // Macro #include // MITK #include #include #include // Microservices #include #include #include namespace us { struct PrototypeServiceFactory; } namespace mitk { class CustomMimeType; /** * @brief Base class for creating mitk::BaseData objects from files or streams. * @ingroup IO */ class MITKCORE_EXPORT AbstractFileReader : public mitk::IFileReader { public: void SetInput(const std::string &location) override; void SetInput(const std::string &location, std::istream *is) override; std::string GetInputLocation() const override; std::istream *GetInputStream() const override; MimeType GetRegisteredMimeType() const; /** * @brief Reads a path or stream and creates a list of BaseData objects. * - * This method must be implemented for each specific reader. Call - * GetInputStream() first and check for a non-null stream to read from. - * If the input stream is \c nullptr, use GetInputLocation() to read from a local - * file-system path. - * - * If the reader cannot use streams directly, use GetLocalFileName() instead. - * - * @return The created BaseData objects. - * @throws mitk::Exception - * - * @see GetLocalFileName() - * @see IFileReader::Read() + * The default implementation of this method (1) calls DoRead() + * (Implement the specific reader operation there) and (2) it addes general + * meta information about the loading process. */ - std::vector> Read() override = 0; + std::vector> Read() override; DataStorage::SetOfObjects::Pointer Read(mitk::DataStorage &ds) override; ConfidenceLevel GetConfidenceLevel() const override; Options GetOptions() const override; us::Any GetOption(const std::string &name) const override; void SetOptions(const Options &options) override; void SetOption(const std::string &name, const us::Any &value) override; void AddProgressCallback(const ProgressCallback &callback) override; void RemoveProgressCallback(const ProgressCallback &callback) override; /** * Associate this reader with the MIME type returned by the current IMimeTypeProvider * service for the provided extension if the MIME type exists, otherwise registers * a new MIME type when RegisterService() is called. * * If no MIME type for \c extension is already registered, a call to RegisterService() * will register a new MIME type and associate this reader instance with it. The MIME * type id can be set via SetMimeType() or it will be auto-generated using \c extension, * having the form "application/vnd.mitk.". * * @param extension The file extension (without a leading period) for which a registered * mime-type object is looked up and associated with this reader instance. * @param description A human readable description of this reader. */ us::ServiceRegistration RegisterService(us::ModuleContext *context = us::GetModuleContext()); void UnregisterService(); /** * @return A list of files that were loaded during the last call of Read. Has to be filled by the actual reader class. */ std::vector< std::string > GetReadFiles() override; protected: /** * @brief An input stream wrapper. * * If a reader can only work with input streams, use an instance * of this class to either wrap the specified input stream or * create a new input stream based on the input location in the * file system. */ class MITKCORE_EXPORT InputStream : public std::istream { public: InputStream(IFileReader *writer, std::ios_base::openmode mode = std::ios_base::in); ~InputStream() override; private: std::istream *m_Stream; }; AbstractFileReader(); ~AbstractFileReader() override; AbstractFileReader(const AbstractFileReader &other); /** * Associate this reader instance with the given MIME type. * * If \c mimeType does not provide an extension list, an already * registered mime-type object is used. Otherwise, the first entry in * the extensions list is used to construct a mime-type name and * register it as a new CustomMimeType service object in the default * implementation of RegisterMimeType(). * * @param mimeType The mime type this reader can read. * @param description A human readable description of this reader. * * @throws std::invalid_argument if \c mimeType is empty. * * @see RegisterService */ explicit AbstractFileReader(const CustomMimeType &mimeType, const std::string &description); + /** Method that should be implemented by derived classes and does the real loading. + * This method is called by Read(). + * This method must be implemented for each specific reader. Call + * GetInputStream() first and check for a non-null stream to read from. + * If the input stream is \c nullptr, use GetInputLocation() to read from a local + * file-system path. + * + * If the reader cannot use streams directly, use GetLocalFileName() instead. + * + * @return The created BaseData objects. + * @throws mitk::Exception + * + * @see GetLocalFileName() + * @see IFileReader::Read() + */ + virtual std::vector> DoRead() = 0; + + virtual us::ServiceProperties GetServiceProperties() const; /** * Registers a new CustomMimeType service object. * * This method is called from RegisterService and the default implementation * registers a new mime-type service object if all of the following conditions * are true: * * - TODO * * @param context * @return * @throws std::invalid_argument if \c context is nullptr. */ virtual us::ServiceRegistration RegisterMimeType(us::ModuleContext *context); void SetMimeType(const CustomMimeType &mimeType); /** * @return The mime-type this reader can handle. */ const CustomMimeType *GetMimeType() const; void SetMimeTypePrefix(const std::string &prefix); std::string GetMimeTypePrefix() const; void SetDescription(const std::string &description); std::string GetDescription() const; void SetDefaultOptions(const Options &defaultOptions); Options GetDefaultOptions() const; /** * \brief Set the service ranking for this file reader. * * Default is zero and should only be chosen differently for a reason. * The ranking is used to determine which reader to use if several * equivalent readers have been found. * It may be used to replace a default reader from MITK in your own project. * E.g. if you want to use your own reader for nrrd files instead of the default, * implement it and give it a higher ranking than zero. */ void SetRanking(int ranking); int GetRanking() const; /** * @brief Get a local file name for reading. * * This is a convenience method for readers which cannot work natively * with input streams. If no input stream has been been set, * this method just returns the result of GetLocation(). However, if * SetLocation(std::string, std::istream*) has been called with a non-null * input stream, this method writes the contents of the stream to a temporary * file and returns the name of the temporary file. * * The temporary file is deleted when either SetLocation(std::string, std::istream*) * is called again with a different input stream or the destructor of this * class is called. * * This method does not validate file names set via SetInput(std::string). * * @return A file path in the local file-system for reading. */ std::string GetLocalFileName() const; virtual void SetDefaultDataNodeProperties(DataNode *node, const std::string &filePath); std::vector< std::string > m_ReadFiles; private: AbstractFileReader &operator=(const AbstractFileReader &other); virtual mitk::IFileReader *Clone() const = 0; class Impl; std::unique_ptr d; }; } // namespace mitk #endif /* AbstractFileReader_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Modules/Core/include/mitkGenericIDRelationRule.h b/Modules/Core/include/mitkGenericIDRelationRule.h index 36169d8dd3..f6e2c8fd96 100644 --- a/Modules/Core/include/mitkGenericIDRelationRule.h +++ b/Modules/Core/include/mitkGenericIDRelationRule.h @@ -1,132 +1,98 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkGenericIDRelationRule_h #define mitkGenericIDRelationRule_h #include "mitkPropertyRelationRuleBase.h" namespace mitk { /**This rule class can be used for relations that are only defined on the ID-layer and where no connection on the Data-layer can be defined or deduced. So it can be used for all ID based relations between PropertyProviders that also implement the interface identifiable. In order to be able to use this class for different relation types based on ID, the ruleIDTag is used. It must be specified when creating a rule instance. The ruleIDTag will be used as suffix for the rule ID of the instance and therefore allows to create specific and distinguishable rules instances based on this class. One may also specify the display name and the role names of the instance. If not speficied the default values are used (display name: " relation", source role name: "source of relation", destination role name: "destination of relation") */ class MITKCORE_EXPORT GenericIDRelationRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(GenericIDRelationRule, PropertyRelationRuleBase); itkCloneMacro(Self); mitkNewMacro1Param(Self, const RuleIDType &); mitkNewMacro2Param(Self, const RuleIDType &, const std::string &); mitkNewMacro4Param(Self, const RuleIDType &, const std::string &, const std::string &, const std::string &); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; /** Returns an ID string that identifies the rule class */ RuleIDType GetRuleID() const override; bool IsAbstract() const override; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ std::string GetDisplayName() const override; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ std::string GetSourceRoleName() const override; /** Returns a human readable string that can be used to describe the role of a destination in context of the rule * instance.*/ std::string GetDestinationRoleName() const override; /** Pass through to base implementation of PropertyRelationRuleBase. See PropertyRelationRuleBase::connect documentation for more information. */ RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const; protected: GenericIDRelationRule(const RuleIDType &ruleIDTag); GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName); GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole); ~GenericIDRelationRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; - /** Is called if a instance ID cannot be deduced on the ID-layer. - Implement this method to check which existing relation(s) as Connected_Data exists between - both passed instances. If the passed instances have no - explicit relation of type Connected_Data, an empty vector will be returned. - @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider - pair and rule. But the data layer may be ambiguous and there for muliple relation instances of the rule instance - could match. The implementation of this function should report all relation instances. The calling function - will take care of this violation. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance.*/ - InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, - const IPropertyProvider *destination) const override; - - /** Is called by HasRelation() if no relation of type Connected_ID (GetInstanceID_IDLayer()) or - Connected_Data (GetInstanceID_datalayer()) is evident. - Implement this method to deduce if the passed instances have a relation of type - Implicit_Data. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance. - */ - bool HasImplicitDataRelation(const IPropertyProvider *source, - const IPropertyProvider *destination) const override; - - /**Is called by Connect() to ensure that source has correctly set properties to resemble - the relation on the data layer. This means that the method should set the properties that describe - and encode the relation on the data layer (data-layer-specific relation properties). - If the passed instance are already connected, the old settings should be - overwritten. Connect() will ensure that source and destination are valid pointers. - @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance - is ensured. - @pre source must be a valid instance. - @pre destination must be a valid instance.*/ + using DataRelationUIDVectorType = PropertyRelationRuleBase::DataRelationUIDVectorType; + DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override; + void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override; - /**This method is called by Disconnect() to remove all properties of the relation from the source that - are set by Connect_datalayer(). - @remark All RII-properties of this relation will removed by Disconnect() after this method call. - If the relationUID is not part of the source. Nothing will be changed. Disconnect() ensures that source is a valid - pointer if called. - @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ - void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const override; + void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType& relationUID) const override; bool IsSupportedRuleID(const RuleIDType& ruleID) const override; itk::LightObject::Pointer InternalClone() const override; private: RuleIDType m_RuleIDTag; std::string m_DisplayName; std::string m_SourceRole; std::string m_DestinationRole; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkGeometryDataReaderService.h b/Modules/Core/include/mitkGeometryDataReaderService.h index 6bdb38da25..1c7f8e23cb 100644 --- a/Modules/Core/include/mitkGeometryDataReaderService.h +++ b/Modules/Core/include/mitkGeometryDataReaderService.h @@ -1,60 +1,61 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkGeometryDataReaderService_h #define mitkGeometryDataReaderService_h // MITK #include #include class TiXmlElement; namespace mitk { /** * @internal * * @brief reads XML representations of mitk::GeometryData from a file/stream. * * To be used via IOUtil. * * Reader for XML files containing one or multiple XML represenations of * mitk::GeometryData. If multiple mitk::GeometryData objects are stored in one file, * these are assigned to multiple BaseData objects. * * @sa Geometry3DToXML * * @ingroup IO */ class GeometryDataReaderService : public AbstractFileReader { public: GeometryDataReaderService(); ~GeometryDataReaderService() override; using AbstractFileReader::Read; - std::vector> Read() override; /** * @brief Provides the MIME type for reader and writer. */ static CustomMimeType GEOMETRY_DATA_MIMETYPE(); + protected: + std::vector> DoRead() override; private: GeometryDataReaderService(const GeometryDataReaderService &other); GeometryDataReaderService *Clone() const override; }; } #endif diff --git a/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h b/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h new file mode 100644 index 0000000000..166bd66d3e --- /dev/null +++ b/Modules/Core/include/mitkIOMetaInformationPropertyConstants.h @@ -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. + +============================================================================*/ + +#ifndef MITKIOMETAINFORMATIONCONSTANTS_H_ +#define MITKIOMETAINFORMATIONCONSTANTS_H_ + +#include + +#include "mitkPropertyKeyPath.h" + +namespace mitk +{ + /** + * @ingroup IO + * @brief The IOMetaInformationPropertyConstants struct + */ + struct MITKCORE_EXPORT IOMetaInformationPropertyConstants + { + //Path to the property containing the name of the reader used + static PropertyKeyPath READER_DESCRIPTION(); + //Path to the property containing the version of mitk used to read the data + static PropertyKeyPath READER_VERSION(); + //Path to the property containing the mine name detected used to read the data + static PropertyKeyPath READER_MIME_NAME(); + //Path to the property containing the mime category detected to read the data + static PropertyKeyPath READER_MIME_CATEGORY(); + //Path to the property containing the input location if loaded by file used to read the data + static PropertyKeyPath READER_INPUTLOCATION(); + //Path to the properties containing the reader optins used to read the data + static PropertyKeyPath READER_OPTION_ROOT(); + static PropertyKeyPath READER_OPTIONS_ANY(); + }; +} + +#endif // MITKIOCONSTANTS_H_ diff --git a/Modules/Core/include/mitkImagePixelReadAccessor.h b/Modules/Core/include/mitkImagePixelReadAccessor.h index 3caf132c2c..2d8fc38968 100644 --- a/Modules/Core/include/mitkImagePixelReadAccessor.h +++ b/Modules/Core/include/mitkImagePixelReadAccessor.h @@ -1,191 +1,205 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKIMAGEPIXELREADACCESSOR_H #define MITKIMAGEPIXELREADACCESSOR_H #include "mitkImagePixelAccessor.h" #include "mitkImageReadAccessor.h" namespace mitk { class Image; /** * @brief Gives locked and index-based read access for a particular image part. * The class provides several set- and get-methods, which allow an easy pixel access. * It needs to know about pixel type and dimension of its image at compile time. * @tparam TPixel defines the PixelType * @tparam VDimension defines the dimension for accessing data * @ingroup Data */ template class ImagePixelReadAccessor : public ImagePixelAccessor { friend class Image; public: typedef ImagePixelAccessor ImagePixelAccessorType; typedef itk::SmartPointer ImagePointer; typedef itk::SmartPointer ImageConstPointer; /** \brief Instantiates a mitk::ImageReadAccessor (see its doxygen page for more details) * \param Image::Pointer specifies the associated Image * \param ImageDataItem* specifies the allocated image part * \param OptionFlags properties from mitk::ImageAccessorBase::Options can be chosen and assembled with bitwise * unification. * \throws mitk::Exception if the Constructor was created inappropriately * \throws mitk::MemoryIsLockedException if requested image area is exclusively locked and * mitk::ImageAccessorBase::ExceptionIfLocked is set in OptionFlags * * Includes a check if typeid of PixelType coincides with templated TPixel * and a check if VDimension equals to the Dimension of the Image. */ ImagePixelReadAccessor(ImageConstPointer iP, const ImageDataItem *iDI = nullptr, int OptionFlags = ImageAccessorBase::DefaultBehavior) : ImagePixelAccessor(iP, iDI), m_ReadAccessor(iP, iDI, OptionFlags) { } ImagePixelReadAccessor(ImagePointer iP, const ImageDataItem *iDI = nullptr, int OptionFlags = ImageAccessorBase::DefaultBehavior) : ImagePixelAccessor(iP.GetPointer(), iDI), m_ReadAccessor(iP, iDI, OptionFlags) { } ImagePixelReadAccessor(Image *iP, const ImageDataItem *iDI = nullptr, int OptionFlags = ImageAccessorBase::DefaultBehavior) : ImagePixelAccessor(iP, iDI), m_ReadAccessor(mitk::Image::ConstPointer(iP), iDI, OptionFlags) { } ImagePixelReadAccessor(const Image *iP, const ImageDataItem *iDI = nullptr, int OptionFlags = ImageAccessorBase::DefaultBehavior) : ImagePixelAccessor(iP, iDI), m_ReadAccessor(iP, iDI, OptionFlags) { } /** Destructor informs Image to unlock memory. */ ~ImagePixelReadAccessor() override {} /** Returns a const reference to the pixel at given index. */ const TPixel &GetPixelByIndex(const itk::Index &idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); return *(((TPixel *)m_ReadAccessor.m_AddressBegin) + offset); } itk::VariableLengthVector GetConsecutivePixelsAsVector(const itk::Index &idx, int nrComponents) const { return itk::VariableLengthVector( (TPixel *)m_ReadAccessor.m_AddressBegin + ImagePixelAccessorType::GetOffset(idx) * m_ReadAccessor.GetImage()->GetPixelType().GetNumberOfComponents(), nrComponents); } /** Extends GetPixel by integrating index validation to prevent overflow. * \throws mitk::Exception in case of overflow */ const TPixel &GetPixelByIndexSafe(const itk::Index &idx) const { unsigned int offset = ImagePixelAccessorType::GetOffset(idx); TPixel *targetAddress = ((TPixel *)m_ReadAccessor.m_AddressBegin) + offset; if (!(targetAddress >= m_ReadAccessor.m_AddressBegin && targetAddress < m_ReadAccessor.m_AddressEnd)) { mitkThrow() << "ImageAccessor Overflow: image access exceeds the requested image area at " << idx << "."; } return *targetAddress; } /** Returns a const reference to the pixel at given world coordinate - works only with three-dimensional * ImageAccessor */ const TPixel &GetPixelByWorldCoordinates(mitk::Point3D position) { itk::Index<3> itkIndex; m_ReadAccessor.GetImage()->GetGeometry()->WorldToIndex(position, itkIndex); return GetPixelByIndex(itkIndex); } /** Returns a const reference to the pixel at given world coordinate - works only with four-dimensional * ImageAccessor */ const TPixel &GetPixelByWorldCoordinates(mitk::Point3D position, unsigned int timestep) { itk::Index<3> itkIndex; m_ReadAccessor.GetImage()->GetGeometry()->WorldToIndex(position, itkIndex); if (m_ReadAccessor.GetImage()->GetTimeSteps() < timestep) { timestep = m_ReadAccessor.GetImage()->GetTimeSteps(); } itk::Index<4> itk4Index; for (int i = 0; i < 3; ++i) itk4Index[i] = itkIndex[i]; itk4Index[3] = timestep; return GetPixelByIndex(itk4Index); } /** \brief Gives const access to the data. */ inline const TPixel *GetData() const { return static_cast(m_ReadAccessor.m_AddressBegin); } protected: // protected members private: ImageReadAccessor m_ReadAccessor; ImagePixelReadAccessor &operator=(const ImagePixelReadAccessor &); // Not implemented on purpose. ImagePixelReadAccessor(const ImagePixelReadAccessor &); }; /** Static method that gets direct access to a single pixel value. * The value is not guaranteed to be in a well-defined state and is automatically casted to mitk::ScalarType * The method can be called by the macros in mitkPixelTypeMultiplex.h */ template mitk::ScalarType FastSinglePixelAccess(mitk::PixelType, mitk::Image::Pointer im, ImageDataItem *item, itk::Index<3> idx, mitk::ScalarType &val, int component = 0) { - mitk::ImagePixelReadAccessor imAccess(im, item, mitk::ImageAccessorBase::IgnoreLock); + ImagePixelReadAccessor imAccess(im, item, mitk::ImageAccessorBase::IgnoreLock); + val = imAccess.GetConsecutivePixelsAsVector(idx, component + 1).GetElement(component); + return val; + } + + /** Const overload of FastSinglePixelAccess*/ + template + mitk::ScalarType FastSinglePixelAccess(mitk::PixelType, + mitk::Image::ConstPointer im, + const ImageDataItem* item, + itk::Index<3> idx, + mitk::ScalarType& val, + int component = 0) + { + ImagePixelReadAccessor imAccess(im, item, mitk::ImageAccessorBase::IgnoreLock); val = imAccess.GetConsecutivePixelsAsVector(idx, component + 1).GetElement(component); return val; } /** Const overload of FastSinglePixelAccess*/ template mitk::ScalarType FastSinglePixelAccess(mitk::PixelType, mitk::Image::ConstPointer im, const ImageDataItem* item, itk::Index<3> idx, mitk::ScalarType& val, int component = 0) { mitk::ImagePixelReadAccessor imAccess(im, item, mitk::ImageAccessorBase::IgnoreLock); val = imAccess.GetConsecutivePixelsAsVector(idx, component + 1).GetElement(component); return val; } } #endif // MITKIMAGEPIXELREADACCESSOR_H diff --git a/Modules/Core/include/mitkItkImageIO.h b/Modules/Core/include/mitkItkImageIO.h index a1c488f9fb..cefc222130 100644 --- a/Modules/Core/include/mitkItkImageIO.h +++ b/Modules/Core/include/mitkItkImageIO.h @@ -1,77 +1,79 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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. */ 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; - std::vector> Read() override; 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/mitkLocaleSwitch.h b/Modules/Core/include/mitkLocaleSwitch.h index 96af6c09b6..601ac02d63 100644 --- a/Modules/Core/include/mitkLocaleSwitch.h +++ b/Modules/Core/include/mitkLocaleSwitch.h @@ -1,61 +1,72 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 __mitkLocaleSwitch_h #define __mitkLocaleSwitch_h #include "MitkCoreExports.h" namespace mitk { /** \brief Convenience class to temporarily change the current locale. This helper class can be used to switch to a specific locale for a couple of operations. Once the class is destroyed, the previous locale will be restored. This avoids calling or forgetting to call setlocale() in multiple return locations. Typically this is used to switch to a "C" locale when parsing or printing numbers, in order to consistently get "." and not "," as a decimal separator. + WARNING: Please be aware that using setlocale and there for is not thread + safe. So use this class with care (see tast T24295 for more information. + This switch is especially use full if you have to deal with third party code + where you have to controll the locale via set locale \code - - std::string toString(int number) { mitk::LocaleSwitch localeSwitch("C");// installs C locale until the end of the function - std::stringstream parser; - parser << number; + ExternalLibraryCall(); //that might throw or what ever. + } + \endcode + If you just want to control you own stringstream operations use imbue instead, as it is + threadsafe. E.G.: + \code + std::string toString(int number) + { + std::ostringstream parser; + parser.imbue(std::locale("C")); + parser << number; return parser.str(); } \endcode */ struct MITKCORE_EXPORT LocaleSwitch { explicit LocaleSwitch(const char *newLocale); ~LocaleSwitch(); LocaleSwitch(LocaleSwitch &) = delete; LocaleSwitch operator=(LocaleSwitch &) = delete; private: struct Impl; Impl *m_LocaleSwitchImpl; }; } #endif // __mitkLocaleSwitch_h diff --git a/Modules/Core/include/mitkPropertyKeyPath.h b/Modules/Core/include/mitkPropertyKeyPath.h index 525503f634..134bb8e9ff 100644 --- a/Modules/Core/include/mitkPropertyKeyPath.h +++ b/Modules/Core/include/mitkPropertyKeyPath.h @@ -1,214 +1,216 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkPropertyKeyPath_h #define mitkPropertyKeyPath_h #include #include #include #include namespace mitk { /** @brief Class that can be used to specify nested or wild carded property keys. E.g. * for the use in context of the property persistence service or the property relation service.\n * Following assumptions are made /preconditions are defined: * - A property key is partitioned by "." into nodes (c.f. visualization of property keys in the PropertyView). * - A node can either be an element or a selection. * - An element has a name (alphanumric, - and space; "A-Za-z0-9- ") or is wildcarded ("*") * - A selection is either an index (e.g. "[1]") or a wildcard ("[*]"). * * Selections are used to indicate that the preceding element has multiple occurences and which occurence is meant. * Example property keys would be: * - prop : A simple property key * - prop.subprop1 : A property key consting of two nodes * - prop.* : Any property key that starts with a node "prop" * - prop.sub.[2] : A property key that starts with a node "prop" and a has a second node that is selection and has * the index 2. * - prop.sub.[*] : Any property key that starts with a node "prop" and a has a second node that is selection (with * any index). * * To build a path one may use the Add* method to build up the PropertyKeyPath element by element.\n * "first.*.third.[3]" would be equivalent to * propKeyPath.AddElement("first"); * propKeyPath.AddAnyElement(); * propKeyPath.AddSelection("third",3);\n * or the inline version * propKeyPath.AddElement("first").AddAnyElement().AddSelection("third",3); */ class MITKCORE_EXPORT PropertyKeyPath final { public: using ItemSelectionIndex = std::size_t; using ElementNameType = std::string; struct MITKCORE_EXPORT NodeInfo { enum class NodeType { Invalid = 0, //*< Node does not exist or is invalid. Element, //*< Selects an specific element given the node name. ElementSelection, //*< Selects an specific item in a sequence of items and has a item selector ("[n]"). AnySelection, //*< Selects all items of a specific element ("[*]"). AnyElement //*< Selects any element/item. Node name is wildcarded ("*"); item selection as well implictily. }; NodeType type; ElementNameType name; ItemSelectionIndex selection; NodeInfo(); NodeInfo(const ElementNameType &name, NodeType type = NodeType::Element, ItemSelectionIndex index = 0); bool Matches(const NodeInfo &right) const; bool operator==(const NodeInfo &right) const; }; using NodeInfoVectorType = std::vector; using PathIndexType = NodeInfoVectorType::size_type; /** Returns if the PropertyKeyPath is empty.*/ bool IsEmpty() const; /** Returns if the path is explicit (has no wildcards).*/ bool IsExplicit() const; /** Returns if the path has any nodes with item selection wild cards ([*]).*/ bool HasItemSelectionWildcardsOnly() const; /** Number of path nodes the PropertyKeyPath contains.*/ PathIndexType GetSize() const; /** Adds a new node to the end of the path. \param [in] newNode Reference to the node that should be added. \return Returns the index of the newly added node.*/ PathIndexType AddNode(const NodeInfo &newNode); /** Function returns the node info of a path node specified by the index * within the PropertyKeyPath. * \pre Passed index must not be out of bounds. * \param [in] index Index of the node whose info should be retrieved. * \return Info of the specified path node. If the index is out of bound an InvalidPathNode exception will be * thrown.*/ const NodeInfo &GetNode(const PathIndexType &index) const; /** Function returns the node info of a path node specified by the index * within the PropertyKeyPath. * \pre Passed index must not be out of bounds. * \param [in] index Index of the node whose info should be retrieved. * \return Info of the specified path node. If the index is out of bound an InvalidPathNode exception will be * thrown.*/ NodeInfo &GetNode(const PathIndexType &index); /** Function returns the node info of the first path node within the PropertyKeyPath. * \pre PropertyKeyPath must not be empty. * \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/ NodeInfo &GetFirstNode(); /** Function returns the node info of the first path node within the PropertyKeyPath. * \pre PropertyKeyPath must not be empty. * \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/ const NodeInfo &GetFirstNode() const; /** Function returns the node info of the last path node within the PropertyKeyPath. * \pre PropertyKeyPath must not be empty. * \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/ NodeInfo &GetLastNode(); /** Function returns the node info of the last path node within the PropertyKeyPath. * \pre PropertyKeyPath must not be empty. * \return Info of the first path node. If the path is empty, an InvalidPathNode exception will be thrown.*/ const NodeInfo &GetLastNode() const; const NodeInfoVectorType &GetNodes() const; /**Compares two PropertyKeyPaths for real equality. So it is a string comparison of their string conversion.*/ bool operator==(const PropertyKeyPath &path) const; /**Operation equals like comparing the ToStr() results with operator <.*/ bool operator<(const PropertyKeyPath &right) const; /**Operation equals like comparing the ToStr() results with operator <=.*/ bool operator<=(const PropertyKeyPath &right) const; /**Operation equals like comparing the ToStr() results with operator >=.*/ bool operator>=(const PropertyKeyPath &right) const; /**Operation equals like comparing the ToStr() results with operator >.*/ bool operator>(const PropertyKeyPath &right) const; /**Checks if two PropertyKeyPaths specify the same node. Hence all wildcards will be processed.\n * E.G.: "item1.child1.grandChild2" == "item1.*.grandChild2" is true. * \remark If you want to check if two paths are "truly" equal and not only equal in terms of * pointing to the same node, use the member function operator ==().*/ bool Equals(const PropertyKeyPath &path) const; PropertyKeyPath &operator=(const PropertyKeyPath &path); /** Appends an "any element" to the path instance.*/ PropertyKeyPath &AddAnyElement(); /** Appends an element with the passed name to the path instance.*/ PropertyKeyPath &AddElement(const ElementNameType &name); /** Appends an element with the passed name and any selection to the path instance.*/ PropertyKeyPath &AddAnySelection(const ElementNameType &name); /** Appends an element with the passed name and selection index to the path instance.*/ PropertyKeyPath &AddSelection(const ElementNameType &name, ItemSelectionIndex index); PropertyKeyPath(); PropertyKeyPath(const PropertyKeyPath &path); + /** overload constructor that supports simple key pathes consisting only of elements.*/ + PropertyKeyPath(const std::initializer_list< ElementNameType >& list); ~PropertyKeyPath(); void Reset(); protected: NodeInfoVectorType m_NodeInfos; static bool PropertyKeyPathsMatch(const PropertyKeyPath &left, const PropertyKeyPath &right); }; class MITKCORE_EXPORT InvalidPathNodeException : public mitk::Exception { public: mitkExceptionClassMacro(InvalidPathNodeException, mitk::Exception); }; MITKCORE_EXPORT std::ostream &operator<<(std::ostream &os, const PropertyKeyPath &path); /**Helper function that converts a path PropertyKeyPath into a regex string that can be used to search for property keys (using std::regex) that are matched by the PropertyKeyPath. This function is used in context of the property persistence service.*/ MITKCORE_EXPORT std::string PropertyKeyPathToPropertyRegEx(const PropertyKeyPath &tagPath); /**Helper function that converts a path PropertyKeyPath into a regex string that can be used to search for property persistence keys (using std::regex) that are matched by the PropertyKeyPath. This function is used in context of the property persistence service.*/ MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceKeyRegEx(const PropertyKeyPath &tagPath); /**Helper function that converts a path PropertyKeyPath into a regex that can be used as key template in a PropertyPersistanceInfo. This function is used in context of the property persistence service.*/ MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceKeyTemplate(const PropertyKeyPath &tagPath); /**Helper function that converts a path PropertyKeyPath into a regex that can be used as name template in a PropertyPersistanceInfo. This function is used in context of the property persistence service.*/ MITKCORE_EXPORT std::string PropertyKeyPathToPersistenceNameTemplate(const PropertyKeyPath &tagPath); /** Converts the passed property name into a tag path. If the property name cannot be converted into a valid path, the returned path is empty.*/ MITKCORE_EXPORT PropertyKeyPath PropertyNameToPropertyKeyPath(const std::string &propertyName); /** returns the correct property name for a given PropertyKeyPath instance. */ MITKCORE_EXPORT std::string PropertyKeyPathToPropertyName(const PropertyKeyPath &tagPath); } // namespace mitk #endif diff --git a/Modules/Core/include/mitkPropertyRelationRuleBase.h b/Modules/Core/include/mitkPropertyRelationRuleBase.h index 05e9523af5..4758746f44 100644 --- a/Modules/Core/include/mitkPropertyRelationRuleBase.h +++ b/Modules/Core/include/mitkPropertyRelationRuleBase.h @@ -1,337 +1,374 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkPropertyRelationRuleBase_h #define mitkPropertyRelationRuleBase_h #include "mitkIPropertyOwner.h" #include "mitkIdentifiable.h" #include "mitkException.h" #include "mitkNodePredicateBase.h" #include "mitkPropertyKeyPath.h" #include #include namespace mitk { /**Base class to standardize/abstract/encapsulate rules and business logic to detect and define (property/data based) relations in MITK. Following important definitions must be regarded when using/implementing/specifing rule classes: - Releations represented by rules are directed relations that point from a source IPropertyOwner (Source) to a destination IPropertyOwner (Destination). - Rule can be abstract (indicated by IsAbstract()) or concrete. Abstract rules cannot be used to connect relations. Abstract rules can only be used to detect/indicate or disconnect relations. Therefore, in contrast to concrete rules, abstract rules can be used to indicate several relations that are established be "derived" rules. See e.g. GenericIDRelationRule: in its abstract state it cannot connect but be used to detect any type of generic ID relation. - A concrete rule ID (rule ID of a concrete rule) always "implements" a concrete relation type. E.g. In DICOM the way to express the source image relation to an input image and to a mask would be nearly the same and only differs by the encoded purpose. One may implement an interim or joined class that manages the mutual stuff, but the registered instances must be one concrete rule for "DICOM source input image" and one concrete rule for "DICOM source mask" and both rules must have distinct rule IDs. - Source may have several relations of a rule to different Destinations. Destination may have several relations of a rule from different Sources. But a specific source destination pair may have only one relation of a specific rule id (concrete rule). A specific source destination pair may however have multiple relations for an abstract rule. - The deletion of a Destination in the storage does not remove the relation implicitly. It becomes a "zombie" relation but it should still be documented, even if the destination is unknown. One has to explicitly disconnect a zombie relation to get rid of it. - Each relation has its own UID (relationUID) that can be used to address it. The basic concept of the rule design is that we have two layers of relation identification: Layer 1 is the ID-layer which uses the IIdentifiable interface and UIDs if available to encode "hard" relations. Layer 2 is the Data-layer which uses the properties of Source and Destination to deduce if there is a relation of the rule type. - The ID-layer is completely implemented by this base class. The base class only falls back to the Data-layer - (implemented by the concrete rule class) if the ID-layer is not sufficient or it is explicitly stated. + The ID-layer is completely implemented by this base class. The base class falls back to the Data-layer + (implemented by the concrete rule class) if the ID-layer is not sufficient or it is explicitly stated to (only) + look at the data layer. Reasons for the introduction of the ID-layer are: 1st, data-defined relations may be weak (several Destinations are possible; e.g. DICOM source images may point to several loaded mitk images). But if explicitly a relation was connected it should be deduceable. 2nd, checks on a UID are faster then unnecessary data deduction. Rules use relation instance identifing (RII) properties in order to manage their relations that are stored in the Source. The RII-properties follow the following naming schema: - "MITK.Relations...[relationUID|destinationUID|ruleID|]" + "MITK.Relations..[relationUID|destinationUID|ruleID|]" - : The unique index of the relation for the Source. Used to assign/group the properties to their relation. In the default implementation of this class the instance id is an positive integer (i>0). - relationUID: The UID of the relation. Set by the ID-layer (so by this class) - destinationUID: The UID of the Destination. Set by the ID-layer (so by this class) if Destination implements IIdentifiable. - ruleID: The identifier of the concrete rule that sets the property. Is specified by the derived class and set automaticaly be this base class. - : Information needed by the Data-layer (so derived classes) to find the relationUID */ class MITKCORE_EXPORT PropertyRelationRuleBase : public itk::Object { public: mitkClassMacroItkParent(PropertyRelationRuleBase, itk::Object); itkCloneMacro(Self); itkCreateAnotherMacro(Self); using RuleIDType = std::string; using RelationUIDType = Identifiable::UIDType; using RelationUIDVectorType = std::vector; /** Enum class for different types of relations. */ enum class RelationType { None = 0, /**< Two IPropertyOwner have no relation under the rule.*/ - Implicit_Data = 1, /**< Two IPropertyOwner have a relation, but it is only deduced from the Data-layer and they - were never explicitly connected.*/ - Connected_Data = 2, /**< Two IPropertyOwner have a relation and there where connected on the Data-layer (so a bit + Data = 1, /**< Two IPropertyOwner have a relation, but it is "only" deduced from the Data-layer (so a bit "weaker" as ID). Reasons for the missing ID connection could be that Destintination has not IIdentifiable implemented.*/ - Connected_ID = 4 /**< Two IPropertyOwner have a relation and are fully explictly connected.*/ + ID = 2, /**< Two IPropertyOwner have a relation and are explictly connected via the ID of IIdentifiable of the Destination.*/ + Complete = 3 /**< Two IPropertyOwner have a relation and are fully explictly connected (via data layer and ID layer).*/ }; + using RelationVectorType = std::vector; /** Returns the generic root path for relation rules ("MITK.Relations").*/ static PropertyKeyPath GetRootKeyPath(); /** Returns an ID string that identifies the rule class. @post The returned rule ID must met the preconditions of a PropertyKeyPath element name (see mitk::PropertyKeyPath*/ virtual RuleIDType GetRuleID() const = 0; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ virtual std::string GetDisplayName() const = 0; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ virtual std::string GetSourceRoleName() const = 0; /** Returns a human readable string that can be used to describe the role of a destionation in context of the rule * instance.*/ virtual std::string GetDestinationRoleName() const = 0; /** Returns if the instance is a abstract rule (true). Default implementation is true. Overwrite and reimplement if another behavior is needed.*/ virtual bool IsAbstract() const; /** This method checks if owner is eligible to be a Source for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsSourceCandidate(const IPropertyProvider *owner) const; /** This method checks if owner is eligible to be a Destination for the rule. The default implementation returns a True for every valid IPropertyProvider (so only a null_ptr results into false). May be reimplement by derived rules if they have requirements on potential Sources).*/ virtual bool IsDestinationCandidate(const IPropertyProvider *owner) const; - /** Returns true if the passed owner is a Source of a relation defined by the rule so has at least one relation of - type Connected_Data or Connected_ID. + /** Returns true if the passed owner is a Source of a relation defined by the rule. @pre owner must be a pointer to a valid IPropertyProvider instance.*/ bool IsSource(const IPropertyProvider *owner) const; - /** Returns the relation type of the passed IPropertyOwner instances. + /** Returns all relation types of the passed IPropertyOwner instances. + @remark Abstract rules may have several relationtypes between the instances (from different supported concrete rules), that cover + both ID and Data relations; thus it returns a vector of RelationTypes. + @result Vector of all relation types that exist between the given instances. Empty vector equals none relation at all. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance. */ - RelationType HasRelation(const IPropertyProvider *source, const IPropertyProvider *destination) const; + RelationVectorType GetRelationTypes(const IPropertyProvider* source, const IPropertyProvider* destination) const; + + /** Indicates if passed IPropertyOwner instances have a relation of a certain type. + @remark Abstract rules may also indicate RelationType::Complete if there + are multiple relations (from different supported concrete rules), that cover + both ID and Data relations. + @param requiredRelation Defines the type of relation that should be present. None: does not matter which one, as long as at least one is present. + Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers. + @pre source must be a pointer to a valid IPropertyProvider instance. + @pre destination must be a pointer to a valid IPropertyProvider instance. + */ + bool HasRelation(const IPropertyProvider *source, const IPropertyProvider *destination, RelationType requiredRelation = RelationType::None) const; /** Returns a vector of relation UIDs for all relations of this rule instance that are defined for - the passed source (so relations of type Connected_Data and Connected_ID). + the passed source. @pre source must be a pointer to a valid IPropertyOwner instance. - */ - RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source) const; + @param layer Defines the layer the relations must be reflected. None: does not matter which one, as long as at least one is present. + Data: Only data layer exclusive connections, ID: Only ID layer exclusive connections. Complete: Only relations that are connected on both layers.*/ + RelationUIDVectorType GetExistingRelations(const IPropertyProvider *source, RelationType layer = RelationType::None) const; /** Returns the relation UID(s) for the passed source and destination of this rule instance. If the rule is abstract multiple relation UIDs might be returned. In case of concrete rule only one relation UID. - If the passed instances have no explicit relation (so of type Connected_Data or Connected_ID), - no ID can be deduced and an empty relation UID vector is returned. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance.*/ RelationUIDVectorType GetRelationUIDs(const IPropertyProvider *source, const IPropertyProvider *destination) const; /** Returns the relation UID for the passed source and destination of this rule instance. - If the passed instances have no explicit relation (so of type Connected_Data or Connected_ID), - no ID can be deduced and an exception will be thrown. If more then one relation is found, also - an exception will be thrown. Thus only use this convinience method, if you are sure that one(!) - relation UID can exist. + If the passed instances have no relation, no ID can be deduced and an exception will be thrown. + If more than one relation is found, also an exception will be thrown. Thus only use this convenience method, + if you are sure that one(!) relation UID can exist. @pre source must be a pointer to a valid IPropertyOwner instance. @pre destination must be a pointer to a valid IPropertyOwner instance. - @pre Source and destination have one relation of type Connected_Data or Connected_ID; otherwise + @pre Source and destination have one relation; otherwise if no relation exists a NoPropertyRelationException is thrown; if more than one relation exists a default MITK expception is thrown.*/ RelationUIDType GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const; /**Predicate that can be used to find nodes that qualify as source for that rule (but must not be a source yet). Thus all nodes where IsSourceCandidate() returns true. */ NodePredicateBase::ConstPointer GetSourceCandidateIndicator() const; /**Predicate that can be used to find nodes that qualify as destination for that rule (but must not be a destination yet). Thus all nodes where IsDestinationCandidate() returns true. */ NodePredicateBase::ConstPointer GetDestinationCandidateIndicator() const; - /**Predicate that can be used to find nodes that are Sources of that rule and explicitly connected. + /**Predicate that can be used to find nodes that are Sources of that rule and connected. Thus all nodes where IsSource() returns true.*/ NodePredicateBase::ConstPointer GetConnectedSourcesDetector() const; /**Predicate that can be used to find nodes that are as source related to the passed Destination under the rule @param destination Pointer to the Destination instance that should be used for detection. - @param minimalRelation Defines the minimal strength of the relation type that should be detected. + @param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default); + Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations. @pre Destination must be a valid instance.*/ NodePredicateBase::ConstPointer GetSourcesDetector( - const IPropertyProvider *destination, RelationType minimalRelation = RelationType::Implicit_Data) const; + const IPropertyProvider *destination, RelationType exclusiveRelation = RelationType::None) const; /**Predicate that can be used to find nodes that are as Destination related to the passed Source under the rule @param source Pointer to the Source instance that should be used for detection. - @param minimalRelation Defines the minimal strength of the relation type that should be detected. + @param exclusiveRelation Defines if only special types of relations should detected. None: All relations (default); + Data: must be a data relation (so Data or Complete); ID: must be an ID relation (so ID or Complete); Complete: only complete relations. @pre Destination must be a valid instance.*/ NodePredicateBase::ConstPointer GetDestinationsDetector( - const IPropertyProvider *source, RelationType minimalRelation = RelationType::Implicit_Data) const; + const IPropertyProvider *source, RelationType exclusiveRelation = RelationType::None) const; /**Returns a predicate that can be used to find the Destination of the passed Source for a given relationUID. @param source Pointer to the Source instance that should be used for detection. - @param minimalRelation Defines the minimal strength of the relation type that should be detected. @pre source must be a valid instance. @pre relationUID must identify a relation of the passed source and rule. (This must be in the return of this->GetExistingRelations(source). */ NodePredicateBase::ConstPointer GetDestinationDetector(const IPropertyProvider *source, RelationUIDType relationUID) const; - /**Disconnect the passed instances. Afterwards they have a relation of None or Implicit_Data. - All RII-properties in the source for the passed destination will be removed. + /**Disconnect the passed instances by modifing source. One can specify which layer should be disconnected + via the argument "layer". Default is the complete disconnection. + All RII-properties or properties that define the connection on the data layer in the source + for the passed destination will be removed. @pre source must be a valid instance. - @pre destination must be a valid instance.*/ - void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination) const; + @pre destination must be a valid instance. + @param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection + on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/ + void Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer = RelationType::Complete) const; /**Disconnect the source from the passed relationUID (usefull for "zombie relations"). - All RII-properties in the source for the passed relationUID will be removed. + One can specify which layer should be disconnected + via the argument "layer". Default is the complete disconnection. + All RII-properties or properties that define the connection on the data layer in the source + for the passed destination will be removed. If the relationUID is not part of the source. Nothing will be changed. - @pre source must be a valid instance.*/ - void Disconnect(IPropertyOwner *source, RelationUIDType relationUID) const; + @pre source must be a valid instance. + @param layer Defines the way of disconnection. Data: Only the remove the connection on the data layer. ID: Only remove the connection + on the ID layer. Complete: Remove the connection on all layers. If a connection does not exist on a selected layer, it is silently ignored.*/ + void Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer = RelationType::Complete) const; + + /**Returns the list of PropertyKeyPaths of all properties that are relevant for a given relation. + @param source Pointer to the Source instance that containes the potential properties. + @param relationUID UID of the relation that is relevant for the requested properties. + @param layer Indicates which layer is requested. ID: returns all RII properties that belong to the relation. Data: returns all properties that are relevant/belong to the data layer of the relation. Complete: returns all properties (ID+Data) + @pre source must be a valid instance. + @pre relationUID must identify a relation of the passed source and rule. (This must be in the return of + this->GetExistingRelations(source). */ + std::vector GetReleationPropertyPaths(const IPropertyProvider* source, + RelationUIDType relationUID, RelationType layer = RelationType::Data) const; protected: PropertyRelationRuleBase() = default; ~PropertyRelationRuleBase() override = default; using InstanceIDType = std::string; using InstanceIDVectorType = std::vector; static InstanceIDType NULL_INSTANCE_ID(); /** Returns the instance IDs for the passed source and destination for this rule instance. If the passed source and destination instances has no explicit relation on the ID layer (Connected_ID), an empty vector will be returned. @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider - pair and concrete rule. But for there might be more then one instanceID, because the rule is abstract and - supports multiple rule IDs. 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance + pair and concrete rule. But there might be more then one instanceID because either 1) the rule is abstract and + supports multiple rule IDs or 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance could match. The implementation of this function should report all relation instances. The calling function will take care. @pre source must be a pointer to a valid IPropertyProvider instance. @pre destination must be a pointer to a valid IPropertyProvider instance.*/ InstanceIDVectorType GetInstanceID_IDLayer(const IPropertyProvider *source, const IPropertyProvider *destination) const; - /** Is called if an instance ID cannot be deduced on the ID-layer. - Implement this method to check which existing relation(s) as Connected_Data exists between - both passed instances. If the passed instances have no - explicit relation of type Connected_Data, an empty vector will be returned. - @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider - pair and concrete rule. But for 2 reasons there might be more then one instanceID: 1) the rule is abstract and - supports multiple rule IDs. 2) the data layer may be ambiguous and therefore multiple relation instances of the rule instance - could match. The implementation of this function should report all relation instances. The calling function + using DataRelationUIDVectorType = std::vector< std::pair >; + /** Returns the ReleationUIDs of all relations that are defined by the data layer of source for + this rule instance and, if defined, destination. + If the passed source (and destination) instance has no relation on the data layer, + an empty vector will be returned. + @remark Per definition for property relation rules only 0 or 1 instance should be found for one provider + pair and concrete rule. But there might be more then one instance because either 1) the rule is abstract and + supports multiple rule IDs or 2) the data layer may be ambiguous (e.g. because the destination was not specified) + and therefore multiple relation instances of the rule instance could match. + The implementation of this function should report all relation instances. The calling function will take care. @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance.*/ - virtual InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, - const IPropertyProvider *destination) const = 0; - - /** Is called by HasRelation() if no relation of type Connected_ID (GetInstanceID_IDLayer()) or - Connected_Data (GetInstanceID_datalayer()) is evident. - Implement this method to deduce if the passed instances have a relation of type - Implicit_Data. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance. - */ - virtual bool HasImplicitDataRelation(const IPropertyProvider *source, - const IPropertyProvider *destination) const = 0; + @param destination Destination the find relations should point to. If destination is NULL any relation + on the data layer for this rule and source are wanted. + @param instances_IDLayer List of releation instances that are already defined by the ID layer. The implementation of this + function should only cover releations that are not already resembled in the passed relarions_IDLayer.*/ + virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider * source, + const IPropertyProvider * destination, const InstanceIDVectorType& instances_IDLayer) const = 0; /**Helper function that deduces the relation UID of the given relation instance. If it cannot be deduced an NoPropertyRelationException is thrown.*/ RelationUIDType GetRelationUIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; /**Helper function that deduces the relation instance ID given the relation UID. If it cannot be deduced an NoPropertyRelationException is thrown.*/ InstanceIDType GetInstanceIDByRelationUID(const IPropertyProvider *source, const RelationUIDType &relationUID) const; - /**Explicitly connects the passed instances. Afterwards they have a relation of Connected_Data or Connected_ID (if a - destination implements IIdentifiable). If the passed instance are already connected the old connection will be - overwritten (and raised to the highest possible connection level). + /**Explicitly connects the passed instances. Afterwards they have a relation of Data (if data layer is supported), ID (if a + destination implements IIdentifiable) or Complete (if Data and ID could be connected). If the passed instance are already + connected the old connection will be overwritten (and raised to the highest possible connection level). @remark This method has protected visibility in the base implementation, because it is a design decision of derived rule classes which interface they want to offer for connecting. It may just be made public (e.g. GenericIDRelationRule) or used by own implementations. @pre source must be a valid instance. @pre destination must be a valid instance. @pre the rule instance must not be abstract. @return Return the relation uid of the relation connected by this method call*/ RelationUIDType Connect(IPropertyOwner *source, const IPropertyProvider *destination) const; /**Is called by Connect() to ensure that source has correctly set properties to resemble the relation on the data layer. This means that the method should set the properties that describe and encode the relation on the data layer (data-layer-specific relation properties). If the passed instance are already connected, the old settings should be overwritten. Connect() will ensure that source and destination are valid pointers. @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance is ensured. @pre source must be a valid instance. @pre destination must be a valid instance.*/ virtual void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const = 0; /**This method is called by Disconnect() to remove all properties of the relation from the source that are set by Connect_datalayer(). - @remark All RII-properties of this relation will removed by Disconnect() after this method call. - If the relationUID is not part of the source. Nothing will be changed. Disconnect() ensures that source is a valid + @remark This method should remove all properties that are set for a specific relation by Connect_datalayer(...). + If the relationUID is not part of the source, nothing will be changed. Disconnect() ensures that source is a valid pointer if called. @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ - virtual void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const = 0; + virtual void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const = 0; /** Returns if the passed rule ID is supported/relevant for the rule. Either because it is the very ID of the rule (default implementation) or because it is an abstract rule which also supports the rule ID. @return true: If the rule ID can handle the rule ID. false: the rule does not support the rule ID.*/ virtual bool IsSupportedRuleID(const RuleIDType& ruleID) const; /** Helper function that generates a reg ex that can be used to find a specific RII property for the rule instance. * @param propName If not empty a PropertyPath element will added (with the passed value) after the element. * @param instanceID If not empty only for the reg ex will only valid for the passed instanceID. Otherwise for all.*/ std::string GetRIIPropertyRegEx(const std::string propName = "", const InstanceIDType &instanceID = "") const; /**Helper function that deduces the instance ID out of a property name. If it cannot be deduced an MITK exception is thrown.*/ static InstanceIDType GetInstanceIDByPropertyName(const std::string propName); /**Helper function that retrives the rule ID of a relation instance of a passed source. @pre source must be valid. @pre source must have a relation instance with this ID*/ RuleIDType GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const; + /**Helper function that retrives the destination UID of a relation instance of a passed + source. If the relation has no destination UID, an empty string will be returned. + @pre source must be valid.*/ + std::string GetDestinationUIDByInstanceID(const IPropertyProvider * source, + const InstanceIDType & instanceID) const; itk::LightObject::Pointer InternalClone() const override; - static std::vector GetPropertyKeys(const mitk::IPropertyProvider *owner); + /** helper method that serves as a workaround until T24729 is done. + Please remove if T24728 is done then could directly use owner->GetPropertyKeys() again.*/ + static std::vector GetPropertyKeys(const IPropertyProvider *owner); + + /** Helper method that tries to cast the provider to the Identifiable interface.*/ + const Identifiable* CastProviderAsIdentifiable(const mitk::IPropertyProvider* provider) const; private: /** Creats a relation UID*/ static RelationUIDType CreateRelationUID(); /**Prepares a new relation instance. Therefore an unused and valid instance ID for the passed source will be genarated and a relationUID property with the relationUID will be set to block the instance ID. The instance ID will be returned. @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently.*/ InstanceIDType CreateNewRelationInstance(IPropertyOwner *source, const RelationUIDType &relationUID) const; }; /**Exception that is used by PropertyRelationRuleBase based classes to indicate that two objects have no relation.*/ class NoPropertyRelationException : public Exception { public: mitkExceptionClassMacro(NoPropertyRelationException, Exception) }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkSourceImageRelationRule.h b/Modules/Core/include/mitkSourceImageRelationRule.h index 3b33d42e80..037959ca07 100644 --- a/Modules/Core/include/mitkSourceImageRelationRule.h +++ b/Modules/Core/include/mitkSourceImageRelationRule.h @@ -1,160 +1,129 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 mitkGenericIDRelationRule_h #define mitkGenericIDRelationRule_h #include "mitkPropertyRelationRuleBase.h" #include "mitkImage.h" namespace mitk { /**This rule class can be used for relations that reference an image as source for a destination entity. (e.g. an image that is used to generate the relation source). The ID-layer is supported like for GenericIDReleations. So it can be used for all ID based relations between PropertyProviders that also implement the interface identifiable. In addition the rule uses the data-layer to deduce/define relations. For this layer it uses properties compliant to DICOM. Thus (1) the information is stored in a DICOM Source Image Sequence item (0x0008,0x2112) and (2) the destination must have properties DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016). If the destination does not have this properties, no connection can be made on the data-layer. @remark Please note that PropertyRelationRules and DICOM use the term "source" differently. The DICOM source (image) equals the PropertyRelationRule destination. This is due to an inverted relation direction. So in the context of the SourceImageRelationRule interface a derived data is the source and points to the original image, it derives from. In the context of DICOM this referenced original image would be called source image (as the name of this class). In order to be able to use this class for different relation types (DICOM would call it purposes), the purposeTag is used. It must be specified when creating a rule instance. The purposeTag will be used as suffix for the rule ID of the instance and therefore allows to create specific and distinguishable rules instances based on this class. One may also specify the display name and the role names of the instance. If not specified the default values are used (display name: " relation", source role name: "derived data", destination role name: "source image") */ class MITKCORE_EXPORT SourceImageRelationRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(SourceImageRelationRule, PropertyRelationRuleBase); itkNewMacro(Self); mitkNewMacro1Param(Self, const RuleIDType &); mitkNewMacro2Param(Self, const RuleIDType &, const std::string &); mitkNewMacro4Param(Self, const RuleIDType &, const std::string &, const std::string &, const std::string &); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; /** Returns an ID string that identifies the rule class */ RuleIDType GetRuleID() const override; bool IsAbstract() const override; /** Returns a human readable string that can be used to describe the rule. Does not need to be unique.*/ std::string GetDisplayName() const override; /** Returns a human readable string that can be used to describe the role of a source in context of the rule * instance.*/ std::string GetSourceRoleName() const override; /** Returns a human readable string that can be used to describe the role of a destination in context of the rule * instance.*/ std::string GetDestinationRoleName() const override; bool IsDestinationCandidate(const IPropertyProvider *owner) const override; /** Connects to passed images. @remark destination must specifiy DICOM SOP Instance UIDs (0x0008, 0x0018) and DICOM SOP Class UID (0x0008, 0x0016) in order to establish a connection on the data layer.*/ RelationUIDType Connect(Image *source, const Image *destination) const; protected: SourceImageRelationRule(); SourceImageRelationRule(const RuleIDType &purposeTag); SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName); SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole); ~SourceImageRelationRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; - /** Helper function that returns a vector of all selections of the property DICOM.0008.2112 that refer to destination or all (if no destination is passed).*/ - std::vector GetReferenceSequenceIndices(const IPropertyProvider * source, - const IPropertyProvider * destination = nullptr) const; - - /** Is called if a instance ID cannot be deduced on the ID-layer. - Implement this method to check which existing relation(s) as Connected_Data exists between - both passed instances. If the passed instances have no - explicit relation of type Connected_Data, an empty vector will be returned. - @remark Per definition of property relation rules only 0 or 1 instance should be found for one provider - pair and rule. But the data layer may be ambiguous and there for muliple relation instances of the rule instance - could match. The implementation of this function should report all relation instances. The calling function - will take care of this violation. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance.*/ - InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, - const IPropertyProvider *destination) const override; - - /** Is called by HasRelation() if no relation of type Connected_ID (GetInstanceID_IDLayer()) or - Connected_Data (GetInstanceID_datalayer()) is evident. - Implement this method to deduce if the passed instances have a relation of type - Implicit_Data. - @pre source must be a pointer to a valid IPropertyProvider instance. - @pre destination must be a pointer to a valid IPropertyProvider instance. - */ - bool HasImplicitDataRelation(const IPropertyProvider *source, - const IPropertyProvider *destination) const override; - - /**Is called by Connect() to ensure that source has correctly set properties to resemble - the relation on the data layer. This means that the method should set the properties that describe - and encode the relation on the data layer (data-layer-specific relation properties). - If the passed instance are already connected, the old settings should be - overwritten. Connect() will ensure that source and destination are valid pointers. - @param instanceID is the ID for the relation instance that should be connected. Existance of the relation instance - is ensured. - @pre source must be a valid instance. - @pre destination must be a valid instance.*/ + /** Helper function that returns a vector of all selections of the property DICOM.0008.2112 (and its associated ruleID) that refer to destination or all (if no destination is passed) with a supported RuleID + and which are not already covered by the ignoreInstances.*/ + std::vector > GetReferenceSequenceIndices(const IPropertyProvider * source, + const IPropertyProvider* destination = nullptr, InstanceIDVectorType ignoreInstances = {}) const; + + using DataRelationUIDVectorType = PropertyRelationRuleBase::DataRelationUIDVectorType; + virtual DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override; + void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override; - /**This method is called by Disconnect() to remove all properties of the relation from the source that - are set by Connect_datalayer(). - @remark All RII-properties of this relation will removed by Disconnect() after this method call. - If the relationUID is not part of the source. Nothing will be changed. Disconnect() ensures that source is a valid - pointer if called. - @remark Disconnect() ensures that sourece is valid and only invokes if instance exists.*/ - void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType &instanceID) const override; + void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType& relationUID) const override; bool IsSupportedRuleID(const RuleIDType& ruleID) const override; itk::LightObject::Pointer InternalClone() const override; /**Prepares a new reference to an image on the data layer. Therefore an unused and valid sequence item index for the passed source will be genarated and a relationUID property with the relationUID will be set to block the instance ID. The instance ID will be returned. @remark The method is guarded by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently.*/ PropertyKeyPath::ItemSelectionIndex CreateNewSourceImageSequenceItem(IPropertyOwner *source) const; + std::string GenerateRuleID(const std::string& purpose) const; + private: RuleIDType m_PurposeTag; std::string m_DisplayName; std::string m_SourceRole; std::string m_DestinationRole; }; } // namespace mitk #endif diff --git a/Modules/Core/include/mitkTemporalJoinImagesFilter.h b/Modules/Core/include/mitkTemporalJoinImagesFilter.h new file mode 100644 index 0000000000..b03f905221 --- /dev/null +++ b/Modules/Core/include/mitkTemporalJoinImagesFilter.h @@ -0,0 +1,62 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef TEMPORALJOINIMAGESFILTER_H +#define TEMPORALJOINIMAGESFILTER_H + + +#include "mitkImageToImageFilter.h" +#include "mitkCommon.h" + +#include "MitkCoreExports.h" + +namespace mitk +{ + /** Filter that takes n mitk images as inputs and fuse them to a new image (with n time points). + Preconditions of this filter are, that all input images have the same pixel type and geometry. + The sequence of frames in the output image is the same then the sequence of inputs. + It no time bounds are defined the dynamic image will start at 0 ms and each time step has a duration + of 1 ms.*/ + class MITKCORE_EXPORT TemporalJoinImagesFilter : public ImageToImageFilter + { + public: + mitkClassMacro(TemporalJoinImagesFilter, ImageToImageFilter); + itkFactorylessNewMacro(TemporalJoinImagesFilter); + + typedef std::vector TimeBoundsVectorType; + + itkGetConstMacro(FirstMinTimeBound, mitk::TimePointType); + /**Set custom min time bound for the first time step.*/ + itkSetMacro(FirstMinTimeBound, mitk::TimePointType); + + itkGetConstMacro(MaxTimeBounds, TimeBoundsVectorType); + /**Set custom max time bounds for all time steps.*/ + void SetMaxTimeBounds(const TimeBoundsVectorType &bounds); + + protected: + TemporalJoinImagesFilter(){}; + ~TemporalJoinImagesFilter() override{}; + + void GenerateInputRequestedRegion() override; + + void GenerateOutputInformation() override; + + void GenerateData() override; + + private: + TimeBoundsVectorType m_MaxTimeBounds; + mitk::TimePointType m_FirstMinTimeBound = 0.0; + }; +} + + +#endif // MODELSIGNALIMAGEGENERATOR_H diff --git a/Modules/Core/include/mitkTemporoSpatialStringProperty.h b/Modules/Core/include/mitkTemporoSpatialStringProperty.h index a138685bdf..e261df1331 100644 --- a/Modules/Core/include/mitkTemporoSpatialStringProperty.h +++ b/Modules/Core/include/mitkTemporoSpatialStringProperty.h @@ -1,125 +1,136 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKTEMPOROSPATIALSTRINGPROPERTY_H_HEADER #define MITKTEMPOROSPATIALSTRINGPROPERTY_H_HEADER #include #include "mitkBaseProperty.h" #include #include "mitkTimeGeometry.h" #include namespace mitk { #ifdef _MSC_VER #pragma warning(push) #pragma warning(disable : 4522) #endif /** * @brief Property for time and space resolved string values * @ingroup DataManagement */ class MITKCORE_EXPORT TemporoSpatialStringProperty : public BaseProperty { public: typedef ::itk::IndexValueType IndexValueType; typedef std::string ValueType; mitkClassMacro(TemporoSpatialStringProperty, BaseProperty); itkFactorylessNewMacro(Self); itkCloneMacro(Self); mitkNewMacro1Param(TemporoSpatialStringProperty, const char*); mitkNewMacro1Param(TemporoSpatialStringProperty, const std::string &); /**Returns the value of the first time point in the first slice. * If now value is set it returns an empty string.*/ ValueType GetValue() const; /**Returns the value of the passed time step and slice. If it does not exist and allowedClosed is true * it will look for the closest value. If nothing could be found an empty string will be returned.*/ ValueType GetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime = false, bool allowCloseSlice = false) const; ValueType GetValueBySlice(const IndexValueType &zSlice, bool allowClose = false) const; ValueType GetValueByTimeStep(const TimeStepType &timeStep, bool allowClose = false) const; bool HasValue() const; bool HasValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime = false, bool allowCloseSlice = false) const; bool HasValueBySlice(const IndexValueType &zSlice, bool allowClose = false) const; bool HasValueByTimeStep(const TimeStepType &timeStep, bool allowClose = false) const; - std::vector GetAvailableSlices(const TimeStepType &timeStep) const; + /** return all slices stored for the specified timestep.*/ + std::vector GetAvailableSlices(const TimeStepType& timeStep) const; + /** return all time steps stored for the specified slice.*/ + std::vector GetAvailableTimeSteps(const IndexValueType& slice) const; + /** return all time steps stored in the property.*/ std::vector GetAvailableTimeSteps() const; + /** return all slices stored in the property. @Remark not all time steps may contain all slices.*/ + std::vector GetAvailableSlices() const; void SetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, const ValueType &value); void SetValue(const ValueType &value); std::string GetValueAsString() const override; + /** Inidicates of all values (all time steps, all slices) are the same, or if at least one value stored + in the property is different. If IsUniform==true one can i.a. use GetValueAsString() without the loss of + information to retrieve the stored value.*/ + bool IsUniform() const; + using BaseProperty::operator=; protected: typedef std::map SliceMapType; typedef std::map TimeMapType; TimeMapType m_Values; TemporoSpatialStringProperty(const char *string = nullptr); TemporoSpatialStringProperty(const std::string &s); TemporoSpatialStringProperty(const TemporoSpatialStringProperty &); std::pair CheckValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime = false, bool allowCloseSlice = false) const; private: // purposely not implemented TemporoSpatialStringProperty &operator=(const TemporoSpatialStringProperty &); itk::LightObject::Pointer InternalClone() const override; bool IsEqual(const BaseProperty &property) const override; bool Assign(const BaseProperty &property) override; }; namespace PropertyPersistenceSerialization { /** Serialization of a TemporoSpatialStringProperty into a JSON string.*/ MITKCORE_EXPORT::std::string serializeTemporoSpatialStringPropertyToJSON(const mitk::BaseProperty *prop); } namespace PropertyPersistenceDeserialization { /**Deserialize a passed JSON string into a TemporoSpatialStringProperty.*/ MITKCORE_EXPORT mitk::BaseProperty::Pointer deserializeJSONToTemporoSpatialStringProperty(const std::string &value); } #ifdef _MSC_VER #pragma warning(pop) #endif } // namespace mitk #endif diff --git a/Modules/Core/src/Algorithms/mitkTemporalJoinImagesFilter.cpp b/Modules/Core/src/Algorithms/mitkTemporalJoinImagesFilter.cpp new file mode 100644 index 0000000000..7b22a3760f --- /dev/null +++ b/Modules/Core/src/Algorithms/mitkTemporalJoinImagesFilter.cpp @@ -0,0 +1,122 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +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 "mitkTemporalJoinImagesFilter.h" + +#include + +#include "mitkArbitraryTimeGeometry.h" +#include "mitkImageReadAccessor.h" +#include "mitkTemporoSpatialStringProperty.h" + +void mitk::TemporalJoinImagesFilter::SetMaxTimeBounds(const TimeBoundsVectorType& timeBounds) +{ + m_MaxTimeBounds = timeBounds; + this->Modified(); +} + +void mitk::TemporalJoinImagesFilter::GenerateInputRequestedRegion() +{ + Superclass::GenerateInputRequestedRegion(); + const auto nrOfInputs = this->GetNumberOfInputs(); + for (DataObjectPointerArraySizeType pos = 0; pos < nrOfInputs; ++pos) + { + this->GetInput(pos)->SetRequestedRegionToLargestPossibleRegion(); + } +} + +void mitk::TemporalJoinImagesFilter::GenerateOutputInformation() +{ + mitk::Image::ConstPointer input = this->GetInput(); + mitk::Image::Pointer output = this->GetOutput(); + + const auto nrOfInputs = this->GetNumberOfInputs(); + auto timeBounds = m_MaxTimeBounds; + + if (timeBounds.empty()) + { + timeBounds.resize(nrOfInputs); + std::iota(timeBounds.begin(), timeBounds.end(), 1.0); + } + else if(timeBounds.size() != nrOfInputs) + { + mitkThrow() << "User defined max time bounds do not match the number if inputs (" << nrOfInputs << "). Size of max timebounds is " << timeBounds.size() << ", but it should be " << nrOfInputs << "."; + } + + timeBounds.insert(timeBounds.begin(), m_FirstMinTimeBound); + + auto timeGeo = mitk::ArbitraryTimeGeometry::New(); + timeGeo->ReserveSpaceForGeometries(nrOfInputs); + + for (DataObjectPointerArraySizeType pos = 0; pos < nrOfInputs; ++pos) + { + timeGeo->AppendNewTimeStepClone(this->GetInput(pos)->GetGeometry(), timeBounds[pos], timeBounds[pos + 1]); + } + output->Initialize(input->GetPixelType(), *timeGeo); + + auto newPropList = input->GetPropertyList()->Clone(); + for (DataObjectPointerArraySizeType pos = 1; pos < nrOfInputs; ++pos) + { + const auto otherList = this->GetInput(pos)->GetPropertyList(); + for (const auto& key : otherList->GetPropertyKeys()) + { + auto prop = newPropList->GetProperty(key); + if (prop == nullptr) + { + newPropList->SetProperty(key, otherList->GetProperty(key)->Clone()); + } + else + { + auto tempoSpatialProp = dynamic_cast(prop); + auto oTempoSpatialProp = dynamic_cast(otherList->GetProperty(key)); + if (prop != nullptr && oTempoSpatialProp != nullptr) + { + auto availabelSlices = oTempoSpatialProp->GetAvailableSlices(0); + + for (const auto& sliceID : availabelSlices) + { + tempoSpatialProp->SetValue(pos, sliceID, oTempoSpatialProp->GetValueBySlice(sliceID)); + } + } + //other prop types can be ignored, we only use the values of the first frame. + } + } + } + + output->SetPropertyList(newPropList); +} + + +void mitk::TemporalJoinImagesFilter::GenerateData() +{ + mitk::Image::Pointer output = this->GetOutput(); + mitk::Image::ConstPointer refInput = this->GetInput(); + const auto nrOfInputs = this->GetNumberOfInputs(); + + for (DataObjectPointerArraySizeType pos = 0; pos < nrOfInputs; ++pos) + { + if (!Equal(*(refInput->GetGeometry()), *(this->GetInput(pos)->GetGeometry()), mitk::eps, false)) + { + mitkThrow() << "Cannot fuse images. At least image #" << pos << " has another geometry than the first image."; + } + if (refInput->GetPixelType() != this->GetInput(pos)->GetPixelType()) + { + mitkThrow() << "Cannot fuse images. At least image #" << pos << " has another pixeltype than the first image."; + } + } + + for (DataObjectPointerArraySizeType pos = 0; pos < nrOfInputs; ++pos) + { + mitk::ImageReadAccessor accessor(this->GetInput(pos)); + output->SetVolume(accessor.GetData(), pos); + } +} diff --git a/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp b/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp index 7681bb1fb2..5d5bae9ea7 100644 --- a/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp +++ b/Modules/Core/src/DataManagement/mitkGenericIDRelationRule.cpp @@ -1,92 +1,85 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkGenericIDRelationRule.h" bool mitk::GenericIDRelationRule::IsAbstract() const { return m_RuleIDTag.empty(); }; bool mitk::GenericIDRelationRule::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID() || (IsAbstract() && ruleID.find("IDRelation_") == 0); }; mitk::GenericIDRelationRule::RuleIDType mitk::GenericIDRelationRule::GetRuleID() const { return "IDRelation_" + m_RuleIDTag; }; std::string mitk::GenericIDRelationRule::GetDisplayName() const { return m_DisplayName; }; std::string mitk::GenericIDRelationRule::GetSourceRoleName() const { return m_SourceRole; }; std::string mitk::GenericIDRelationRule::GetDestinationRoleName() const { return m_DestinationRole; }; mitk::GenericIDRelationRule::RelationUIDType mitk::GenericIDRelationRule::Connect(IPropertyOwner *source, const IPropertyProvider *destination) const { return Superclass::Connect(source, destination); }; mitk::GenericIDRelationRule::GenericIDRelationRule(const RuleIDType &ruleIDTag) : GenericIDRelationRule(ruleIDTag, ruleIDTag + " relation"){}; mitk::GenericIDRelationRule::GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName) : GenericIDRelationRule( ruleIDTag, displayName, "source of " + ruleIDTag + " relation", "destination of " + ruleIDTag + " relation"){}; mitk::GenericIDRelationRule::GenericIDRelationRule(const RuleIDType &ruleIDTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole) : m_RuleIDTag(ruleIDTag), m_DisplayName(displayName), m_SourceRole(sourceRole), m_DestinationRole(destinationRole){}; -mitk::GenericIDRelationRule::InstanceIDVectorType mitk::GenericIDRelationRule::GetInstanceID_datalayer( - const IPropertyProvider * /*source*/, const IPropertyProvider * /*destination*/) const +mitk::GenericIDRelationRule::DataRelationUIDVectorType mitk::GenericIDRelationRule::GetRelationUIDs_DataLayer( + const IPropertyProvider * /*source*/, const IPropertyProvider * /*destination*/, const InstanceIDVectorType& /*instances_IDLayer*/) const { // Data layer is not supported by the class. Therefore return empty vector. - return InstanceIDVectorType(); -}; - -bool mitk::GenericIDRelationRule::HasImplicitDataRelation(const IPropertyProvider * /*source*/, - const IPropertyProvider * /*destination*/) const -{ - // Data layer is not supported by the class. - return false; + return DataRelationUIDVectorType(); }; void mitk::GenericIDRelationRule::Connect_datalayer(IPropertyOwner * /*source*/, const IPropertyProvider * /*destination*/, const InstanceIDType & /*instanceID*/) const { // Data layer is not supported by the class. => Do nothing }; -void mitk::GenericIDRelationRule::Disconnect_datalayer(IPropertyOwner * /*source*/, const InstanceIDType & /*instanceID*/) const { +void mitk::GenericIDRelationRule::Disconnect_datalayer(IPropertyOwner * /*source*/, const RelationUIDType & /*relationUID*/) const { // Data layer is not supported by the class. => Do nothing }; itk::LightObject::Pointer mitk::GenericIDRelationRule::InternalClone() const { itk::LightObject::Pointer result = Self::New(this->m_RuleIDTag, this->m_DisplayName, this->m_SourceRole, this->m_DestinationRole).GetPointer(); return result; }; diff --git a/Modules/Core/src/DataManagement/mitkPropertyKeyPath.cpp b/Modules/Core/src/DataManagement/mitkPropertyKeyPath.cpp index 83a2993207..27fa3f08a1 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyKeyPath.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyKeyPath.cpp @@ -1,596 +1,605 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include namespace mitk { PropertyKeyPath::NodeInfo::NodeInfo() : type(NodeType::Invalid), selection(0){}; PropertyKeyPath::NodeInfo::NodeInfo(const ElementNameType &name, NodeType type, ItemSelectionIndex index) : type(type), name(name), selection(index){}; bool PropertyKeyPath::NodeInfo::operator==(const NodeInfo &right) const { if (this->name != right.name) return false; if (this->type != right.type) return false; if (this->selection != right.selection) return false; return true; }; bool PropertyKeyPath::NodeInfo::Matches(const NodeInfo &right) const { if (type == NodeType::Invalid || right.type == NodeType::Invalid) { return false; } else if (type == NodeType::AnyElement || right.type == NodeType::AnyElement) { return true; } else if (name == right.name) { if (type == NodeType::Element && right.type == NodeType::Element) { return true; } else if (selection == right.selection || type == NodeType::AnySelection || right.type == NodeType::AnySelection) { return true; } } return false; }; bool PropertyKeyPath::IsEmpty() const { return m_NodeInfos.empty(); }; bool PropertyKeyPath::IsExplicit() const { for (const auto &pos : m_NodeInfos) { if ((pos.type == NodeInfo::NodeType::AnySelection) || (pos.type == NodeInfo::NodeType::AnyElement)) { return false; } } return true; }; bool PropertyKeyPath::HasItemSelectionWildcardsOnly() const { bool result = false; for (const auto &pos : m_NodeInfos) { if (pos.type == NodeInfo::NodeType::AnyElement) { return false; } result = result || pos.type == NodeInfo::NodeType::AnySelection; } return result; }; PropertyKeyPath::PathIndexType PropertyKeyPath::GetSize() const { return m_NodeInfos.size(); } PropertyKeyPath::PathIndexType PropertyKeyPath::AddNode(const NodeInfo &newNode) { m_NodeInfos.push_back(newNode); return m_NodeInfos.size() - 1; }; const PropertyKeyPath::NodeInfo &PropertyKeyPath::GetNode(const PathIndexType &index) const { if (index >= GetSize()) { mitkThrowException(InvalidPathNodeException) << "Error. Cannot return info of path node. Node index is out of bounds. Index: " << index << "; Path: " << PropertyKeyPathToPropertyName(*this); } return m_NodeInfos[index]; }; PropertyKeyPath::NodeInfo &PropertyKeyPath::GetNode(const PathIndexType &index) { if (index >= this->GetSize()) { mitkThrowException(InvalidPathNodeException) << "Error. Cannot return info of path node. Node index is out of bounds. Index: " << index << "; Path: " << PropertyKeyPathToPropertyName(*this); } return m_NodeInfos[index]; }; const PropertyKeyPath::NodeInfo &PropertyKeyPath::GetFirstNode() const { if (m_NodeInfos.empty()) { mitkThrowException(InvalidPathNodeException) << "Error. Cannot return first path node. Path is empty."; } return this->GetNode(0); }; PropertyKeyPath::NodeInfo &PropertyKeyPath::GetFirstNode() { if (m_NodeInfos.empty()) { mitkThrowException(InvalidPathNodeException) << "Error. Cannot return first path node. Path is empty."; } return this->GetNode(0); }; const PropertyKeyPath::NodeInfo &PropertyKeyPath::GetLastNode() const { if (m_NodeInfos.empty()) { mitkThrowException(InvalidPathNodeException) << "Error. Cannot return last path node. Path is empty."; } return this->GetNode(GetSize() - 1); }; PropertyKeyPath::NodeInfo &PropertyKeyPath::GetLastNode() { if (m_NodeInfos.empty()) { mitkThrowException(InvalidPathNodeException) << "Error. Cannot return last path node. Path is empty."; } return this->GetNode(GetSize() - 1); }; const PropertyKeyPath::NodeInfoVectorType &PropertyKeyPath::GetNodes() const { return m_NodeInfos; }; bool PropertyKeyPath::operator==(const PropertyKeyPath &path) const { return m_NodeInfos == path.m_NodeInfos; }; bool PropertyKeyPath::operator<(const PropertyKeyPath &right) const { auto rightIter = right.m_NodeInfos.cbegin(); const auto rightEnd = right.m_NodeInfos.cend(); for (const auto &leftPos : m_NodeInfos) { if (rightIter == rightEnd) { return false; } if (leftPos.name < rightIter->name) { return true; } if (rightIter->name < leftPos.name) { return false; } if (leftPos.type < rightIter->type) { return true; } if (rightIter->type < leftPos.type) { return false; } if (leftPos.selection < rightIter->selection) { return true; } if (rightIter->selection < leftPos.selection) { return false; } ++rightIter; } return rightIter != rightEnd; } bool PropertyKeyPath::operator>(const PropertyKeyPath &right) const { auto rightIter = right.m_NodeInfos.cbegin(); const auto rightEnd = right.m_NodeInfos.cend(); for (const auto &leftPos : m_NodeInfos) { if (rightIter == rightEnd) return false; if (leftPos.name > rightIter->name) return true; if (rightIter->name > leftPos.name) return false; if (leftPos.type > rightIter->type) return true; if (rightIter->type > leftPos.type) return false; if (leftPos.selection > rightIter->selection) return true; if (rightIter->selection > leftPos.selection) return false; ++rightIter; } return rightIter != rightEnd; } bool PropertyKeyPath::operator>=(const PropertyKeyPath &right) const { return !(*this < right); } bool PropertyKeyPath::operator<=(const PropertyKeyPath &right) const { return !(*this > right); } bool PropertyKeyPath::Equals(const PropertyKeyPath &path) const { return PropertyKeyPathsMatch(*this, path); }; PropertyKeyPath &PropertyKeyPath::operator=(const PropertyKeyPath &path) { if (this != &path) { m_NodeInfos = path.m_NodeInfos; } return *this; }; PropertyKeyPath &PropertyKeyPath::AddAnyElement() { m_NodeInfos.emplace_back("", NodeInfo::NodeType::AnyElement); return *this; }; PropertyKeyPath &PropertyKeyPath::AddElement(const ElementNameType &name) { m_NodeInfos.emplace_back(name, NodeInfo::NodeType::Element); return *this; }; PropertyKeyPath &PropertyKeyPath::AddAnySelection(const ElementNameType &name) { m_NodeInfos.emplace_back(name, NodeInfo::NodeType::AnySelection); return *this; }; PropertyKeyPath &PropertyKeyPath::AddSelection(const ElementNameType &name, ItemSelectionIndex index) { m_NodeInfos.emplace_back(name, NodeInfo::NodeType::ElementSelection, index); return *this; }; PropertyKeyPath::PropertyKeyPath() { this->Reset(); }; PropertyKeyPath::PropertyKeyPath(const PropertyKeyPath &path) { *this = path; }; + PropertyKeyPath::PropertyKeyPath(const std::initializer_list< ElementNameType >& list) + { + this->Reset(); + for (const auto& name : list) + { + this->AddElement(name); + } + } + PropertyKeyPath::~PropertyKeyPath(){}; void PropertyKeyPath::Reset() { m_NodeInfos.clear(); }; bool PropertyKeyPath::PropertyKeyPathsMatch(const PropertyKeyPath &left, const PropertyKeyPath &right) { auto leftPos = left.GetNodes().cbegin(); auto rightPos = right.GetNodes().cbegin(); auto leftEnd = left.GetNodes().cend(); auto rightEnd = right.GetNodes().cend(); while (leftPos != leftEnd && rightPos != rightEnd) { if (!leftPos->Matches(*rightPos)) { break; } ++leftPos; ++rightPos; } if (leftPos == leftEnd && rightPos == rightEnd) { return true; } else { return false; } }; std::ostream &operator<<(std::ostream &os, const PropertyKeyPath &value) { os << PropertyKeyPathToPropertyName(value); return os; }; std::string PropertyKeyPathToPropertyRegEx(const PropertyKeyPath &tagPath) { std::ostringstream nameStream; PropertyKeyPath::PathIndexType i = 0; for (const auto &node : tagPath.GetNodes()) { if (i) { nameStream << "\\."; } ++i; if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnyElement) { nameStream << "([a-zA-Z0-9- ]+)"; } else if (node.type != PropertyKeyPath::NodeInfo::NodeType::Invalid) { nameStream << node.name; if (node.type == PropertyKeyPath::NodeInfo::NodeType::ElementSelection) { nameStream << "\\.\\[" << node.selection << "\\]"; } else if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnySelection) { nameStream << "\\.\\[(\\d*)\\]"; } } else { nameStream << "INVALIDNODE"; } } return nameStream.str(); }; std::string PropertyKeyPathToPersistenceKeyRegEx(const PropertyKeyPath &tagPath) { std::ostringstream nameStream; PropertyKeyPath::PathIndexType i = 0; for (const auto &node : tagPath.GetNodes()) { if (i) { nameStream << "_"; } ++i; if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnyElement) { nameStream << "([a-zA-Z0-9- ]+)"; } else if (node.type != PropertyKeyPath::NodeInfo::NodeType::Invalid) { nameStream << node.name; if (node.type == PropertyKeyPath::NodeInfo::NodeType::ElementSelection) { nameStream << "_\\[" << node.selection << "\\]"; } else if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnySelection) { nameStream << "_\\[(\\d*)\\]"; } } else { nameStream << "INVALIDNODE"; } } return nameStream.str(); }; std::string PropertyKeyPathToPersistenceKeyTemplate(const PropertyKeyPath &tagPath) { std::ostringstream nameStream; int captureGroup = 1; PropertyKeyPath::PathIndexType i = 0; for (const auto &node : tagPath.GetNodes()) { if (i) { nameStream << "_"; } ++i; if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnyElement) { nameStream << "$" << captureGroup++; } else if (node.type != PropertyKeyPath::NodeInfo::NodeType::Invalid) { nameStream << node.name; if (node.type == PropertyKeyPath::NodeInfo::NodeType::ElementSelection) { nameStream << "_[" << node.selection << "]"; } else if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnySelection) { nameStream << "_[$" << captureGroup++ << "]"; } } else { nameStream << "INVALID_NODE"; } } return nameStream.str(); }; std::string PropertyKeyPathToPersistenceNameTemplate(const PropertyKeyPath &tagPath) { std::ostringstream nameStream; int captureGroup = 1; PropertyKeyPath::PathIndexType i = 0; for (const auto &node : tagPath.GetNodes()) { if (i) { nameStream << "."; } ++i; if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnyElement) { nameStream << "$" << captureGroup++; } else if (node.type != PropertyKeyPath::NodeInfo::NodeType::Invalid) { nameStream << node.name; if (node.type == PropertyKeyPath::NodeInfo::NodeType::ElementSelection) { nameStream << ".[" << node.selection << "]"; } else if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnySelection) { nameStream << ".[$" << captureGroup++ << "]"; } } else { nameStream << "INVALID_NODE"; } } return nameStream.str(); }; PropertyKeyPath PropertyNameToPropertyKeyPath(const std::string &propertyName) { PropertyKeyPath result; std::regex reg_element("([a-zA-Z0-9- ]+)"); std::regex reg_anySelection("\\[\\*\\]"); std::regex reg_Selection("\\[(\\d+)\\]"); std::istringstream f(propertyName); std::string subStr; PropertyKeyPath::ElementNameType name = ""; while (getline(f, subStr, '.')) { if (subStr == "*") { if (!name.empty()) { result.AddElement(name); name.clear(); } result.AddAnyElement(); } else { std::smatch sm; if (std::regex_match(subStr, sm, reg_anySelection)) { if (!name.empty()) { result.AddAnySelection(name); name.clear(); } else { // invalid path return PropertyKeyPath(); } } else if (std::regex_match(subStr, sm, reg_Selection)) { if (!name.empty()) { result.AddSelection(name, std::stoi(sm[1])); name.clear(); } else { // invalid path return PropertyKeyPath(); } } else if (std::regex_match(subStr, sm, reg_element)) { if (!name.empty()) { // store the last element and start the next result.AddElement(name); } name = sm[1]; } else { return PropertyKeyPath(); } } } if (!name.empty()) { // add last element result.AddElement(name); } return result; }; std::string PropertyKeyPathToPropertyName(const mitk::PropertyKeyPath &tagPath) { std::ostringstream nameStream; PropertyKeyPath::PathIndexType i = 0; for (const auto &node : tagPath.GetNodes()) { if (i) { nameStream << "."; } ++i; if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnyElement) { nameStream << "*"; } else if (node.type != PropertyKeyPath::NodeInfo::NodeType::Invalid) { nameStream << node.name; if (node.type == PropertyKeyPath::NodeInfo::NodeType::ElementSelection) { nameStream << ".[" << node.selection << "]"; } else if (node.type == PropertyKeyPath::NodeInfo::NodeType::AnySelection) { nameStream << ".[*]"; } } else { nameStream << "INVALID_NODE"; } } return nameStream.str(); }; } // namespace mitk diff --git a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp index 8bfd7d09d6..e05a314b21 100644 --- a/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp +++ b/Modules/Core/src/DataManagement/mitkPropertyRelationRuleBase.cpp @@ -1,758 +1,852 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPropertyRelationRuleBase.h" #include #include #include #include #include #include #include +#include bool mitk::PropertyRelationRuleBase::IsAbstract() const { return true; -}; +} bool mitk::PropertyRelationRuleBase::IsSourceCandidate(const IPropertyProvider *owner) const { return owner != nullptr; -}; +} bool mitk::PropertyRelationRuleBase::IsDestinationCandidate(const IPropertyProvider *owner) const { return owner != nullptr; -}; +} mitk::PropertyKeyPath mitk::PropertyRelationRuleBase::GetRootKeyPath() { return PropertyKeyPath().AddElement("MITK").AddElement("Relations"); -}; +} bool mitk::PropertyRelationRuleBase::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID(); -}; +} std::string mitk::PropertyRelationRuleBase::GetRIIPropertyRegEx(const std::string propName, const InstanceIDType &instanceID) const { auto path = this->GetRootKeyPath(); if (instanceID.empty()) { path.AddAnyElement(); } else { path.AddElement(instanceID); } if (!propName.empty()) { path.AddElement(propName); } return PropertyKeyPathToPropertyRegEx(path); -}; +} //workaround until T24729 is done. Please remove if T24728 is done //then could directly use owner->GetPropertyKeys() again. std::vector mitk::PropertyRelationRuleBase::GetPropertyKeys(const mitk::IPropertyProvider *owner) { std::vector keys; auto sourceCasted = dynamic_cast(owner); if (sourceCasted) { auto sourceData = sourceCasted->GetData(); if (sourceData) { keys = sourceData->GetPropertyKeys(); } else { keys = sourceCasted->GetPropertyKeys(); } } else { keys = owner->GetPropertyKeys(); } return keys; -}; +} //end workaround for T24729 - bool mitk::PropertyRelationRuleBase::IsSource(const IPropertyProvider *owner) const { - if (!owner) - { - mitkThrow() << "Error. Passed owner pointer is NULL"; - } + return !this->GetExistingRelations(owner).empty(); +} - std::vector keys; - //workaround until T24729 is done. Please remove if T24728 is done - keys = GetPropertyKeys(owner); - //end workaround for T24729 +bool mitk::PropertyRelationRuleBase::HasRelation( + const IPropertyProvider* source, const IPropertyProvider* destination, RelationType requiredRelation) const +{ + auto relTypes = this->GetRelationTypes(source, destination); - auto sourceRegExStr = this->GetRIIPropertyRegEx("ruleID"); - auto regEx = std::regex(sourceRegExStr); - for (const auto &key : keys) + if (requiredRelation == RelationType::None) { - if (std::regex_match(key, regEx)) - { - auto idProp = owner->GetConstProperty(key); - auto ruleID = idProp->GetValueAsString(); - if (this->IsSupportedRuleID(ruleID)) - { - return true; - } - } + return !relTypes.empty(); } - return false; -}; + RelationVectorType allowedTypes = { RelationType::Complete }; + if (requiredRelation == RelationType::Data) + { + allowedTypes.emplace_back(RelationType::Data); + } + else if (requiredRelation == RelationType::ID) + { + allowedTypes.emplace_back(RelationType::ID); + } -mitk::PropertyRelationRuleBase::RelationType mitk::PropertyRelationRuleBase::HasRelation( - const IPropertyProvider *source, const IPropertyProvider *destination) const + return relTypes.end() != std::find_first_of(relTypes.begin(), relTypes.end(), allowedTypes.begin(), allowedTypes.end()); +} + +mitk::PropertyRelationRuleBase::RelationVectorType mitk::PropertyRelationRuleBase::GetRelationTypes( + const IPropertyProvider* source, const IPropertyProvider* destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed owner pointer is NULL"; } - RelationType result = RelationType::None; - - if (!this->GetInstanceID_IDLayer(source, destination).empty()) - { // has relations of type Connected_ID; - result = RelationType::Connected_ID; + auto instanceIDs_IDLayer = this->GetInstanceID_IDLayer(source, destination); + auto relIDs_dataLayer = this->GetRelationUIDs_DataLayer(source, destination, {}); + if (relIDs_dataLayer.size() > 1) + { + MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Relation UID: " + << relIDs_dataLayer.front().first; } - if (result == RelationType::None) + bool hasComplete = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) { - auto instanceIDs_data = this->GetInstanceID_datalayer(source, destination); + auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); + auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); - if (instanceIDs_data.size() > 1) + return relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { - MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Instance ID: " - << instanceIDs_data.front(); - } + return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; + }); + }); - if (!instanceIDs_data.empty() && instanceIDs_data.front() != NULL_INSTANCE_ID()) - { // has relations of type Connected_Data; - result = RelationType::Connected_Data; - } - } + bool hasID = instanceIDs_IDLayer.end() != std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) + { + auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); + auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); + + return relIDs_dataLayer.end() == std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) + { + return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; + }); + }); - if (result == RelationType::None) + bool hasData = relIDs_dataLayer.end() != std::find_if(relIDs_dataLayer.begin(), relIDs_dataLayer.end(), [&](const DataRelationUIDVectorType::value_type& relID) { - if (this->HasImplicitDataRelation(source, destination)) - { // has relations of type Connected_Data; - result = RelationType::Implicit_Data; - } + return instanceIDs_IDLayer.end() == std::find_if(instanceIDs_IDLayer.begin(), instanceIDs_IDLayer.end(), [&](const InstanceIDVectorType::value_type& instanceID) + { + auto relID_IDlayer = this->GetRelationUIDByInstanceID(source, instanceID); + auto ruleID_IDlayer = this->GetRuleIDByInstanceID(source, instanceID); + return relID.first == relID_IDlayer && relID.second == ruleID_IDlayer; + }); + }); + + RelationVectorType result; + + if (hasData) + { + result.emplace_back(RelationType::Data); + } + if (hasID) + { + result.emplace_back(RelationType::ID); + } + if (hasComplete) + { + result.emplace_back(RelationType::Complete); } return result; -}; +} mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetExistingRelations( - const IPropertyProvider *source) const + const IPropertyProvider *source, RelationType layer) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } - auto ruleIDRegExStr = this->GetRIIPropertyRegEx("ruleID"); - auto regEx = std::regex(ruleIDRegExStr); - - //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. - const auto keys = GetPropertyKeys(source); - //end workaround for T24729 - RelationUIDVectorType relationUIDs; + InstanceIDVectorType instanceIDs; - for (const auto &key : keys) + if (layer != RelationType::Data) { - if (std::regex_match(key, regEx)) + auto ruleIDRegExStr = this->GetRIIPropertyRegEx("ruleID"); + auto regEx = std::regex(ruleIDRegExStr); + + //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. + const auto keys = GetPropertyKeys(source); + //end workaround for T24729 + + for (const auto& key : keys) { - auto idProp = source->GetConstProperty(key); - auto ruleID = idProp->GetValueAsString(); - if (this->IsSupportedRuleID(ruleID)) + if (std::regex_match(key, regEx)) { - auto instanceID = this->GetInstanceIDByPropertyName(key); - relationUIDs.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); + auto idProp = source->GetConstProperty(key); + auto ruleID = idProp->GetValueAsString(); + if (this->IsSupportedRuleID(ruleID)) + { + auto instanceID = this->GetInstanceIDByPropertyName(key); + instanceIDs.emplace_back(instanceID); + relationUIDs.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); + } } } } - return relationUIDs; -}; + if (layer == RelationType::ID) + { + return relationUIDs; + } + + DataRelationUIDVectorType relationUIDandRuleID_Data; + if (layer != RelationType::ID) + { + relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source, nullptr, instanceIDs); + } + + RelationUIDVectorType relationUIDs_Data; + std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relationUIDs_Data), + [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); + + if (layer == RelationType::Data) + { + return relationUIDs_Data; + } + + std::sort(relationUIDs.begin(), relationUIDs.end()); + std::sort(relationUIDs_Data.begin(), relationUIDs_Data.end()); + + RelationUIDVectorType result; + + if (layer == RelationType::Complete) + { + std::set_intersection(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); + } + else + { + std::set_union(relationUIDs.begin(), relationUIDs.end(), relationUIDs_Data.begin(), relationUIDs_Data.end(), std::back_inserter(result)); + } + + return result; +} mitk::PropertyRelationRuleBase::RelationUIDVectorType mitk::PropertyRelationRuleBase::GetRelationUIDs( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } - RelationUIDVectorType result; + RelationUIDVectorType relUIDs_id; auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); for (const auto instanceID : instanceIDs) { - result.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); + relUIDs_id.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); } - if (result.empty() || this->IsAbstract()) - { - auto instanceIDs_data = this->GetInstanceID_datalayer(source, destination); + DataRelationUIDVectorType relationUIDandRuleID_Data = this->GetRelationUIDs_DataLayer(source,destination,instanceIDs); + RelationUIDVectorType relUIDs_Data; + std::transform(relationUIDandRuleID_Data.begin(), relationUIDandRuleID_Data.end(), std::back_inserter(relUIDs_Data), + [](const DataRelationUIDVectorType::value_type& v) { return v.first; }); - for (const auto instanceID : instanceIDs_data) - { - if (std::find(std::begin(instanceIDs), std::end(instanceIDs), instanceID) == std::end(instanceIDs)) - { - result.push_back(this->GetRelationUIDByInstanceID(source, instanceID)); - } - } - } + std::sort(relUIDs_id.begin(), relUIDs_id.end()); + std::sort(relUIDs_Data.begin(), relUIDs_Data.end()); + + RelationUIDVectorType result; + std::set_union(relUIDs_id.begin(), relUIDs_id.end(), relUIDs_Data.begin(), relUIDs_Data.end(), std::back_inserter(result)); return result; -}; +} mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUID(const IPropertyProvider *source, const IPropertyProvider *destination) const { auto result = this->GetRelationUIDs(source, destination); if (result.empty()) { mitkThrowException(NoPropertyRelationException); } else if(result.size()>1) { mitkThrow() << "Cannot return one(!) relation UID. Multiple relations exists for given rule, source and destination."; } return result[0]; -}; +} mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::NULL_INSTANCE_ID() { return std::string(); }; mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::GetRelationUIDByInstanceID( const IPropertyProvider *source, const InstanceIDType &instanceID) const { RelationUIDType result; if (instanceID != NULL_INSTANCE_ID()) { auto idProp = source->GetConstProperty( PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(instanceID).AddElement("relationUID"))); if (idProp.IsNotNull()) { result = idProp->GetValueAsString(); } } if (result.empty()) { mitkThrowException(NoPropertyRelationException); } return result; -}; +} mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByRelationUID( const IPropertyProvider *source, const RelationUIDType &relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } InstanceIDType result = NULL_INSTANCE_ID(); auto destRegExStr = PropertyKeyPathToPropertyRegEx(GetRootKeyPath().AddAnyElement().AddElement("relationUID")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == relationUID) { if (instance_matches.size()>1) { result = instance_matches[1]; break; } } } } return result; -}; +} mitk::PropertyRelationRuleBase::InstanceIDVectorType mitk::PropertyRelationRuleBase::GetInstanceID_IDLayer( const IPropertyProvider *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } - auto identifiable = dynamic_cast(destination); - - if (!identifiable) - { //This check and pass through to data is needed due to solve T25711. See Task for more information. - //This could be removed at the point we can get rid of DataNodes or they get realy transparent. - auto node = dynamic_cast(destination); - if (node && node->GetData()) - { - identifiable = dynamic_cast(node->GetData()); - } - } + auto identifiable = CastProviderAsIdentifiable(destination); InstanceIDVectorType result; if (identifiable) { // check for relations of type Connected_ID; auto destRegExStr = this->GetRIIPropertyRegEx("destinationUID"); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto destUID = identifiable->GetUID(); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto idProp = source->GetConstProperty(key); if (idProp->GetValueAsString() == destUID) { if (instance_matches.size()>1) { auto instanceID = instance_matches[1]; if (this->IsSupportedRuleID(GetRuleIDByInstanceID(source, instanceID))) { result.push_back(instanceID); } } } } } } return result; -}; +} + +const mitk::Identifiable* mitk::PropertyRelationRuleBase::CastProviderAsIdentifiable(const mitk::IPropertyProvider* destination) const +{ + auto identifiable = dynamic_cast(destination); + + if (!identifiable) + { //This check and pass through to data is needed due to solve T25711. See Task for more information. + //This could be removed at the point we can get rid of DataNodes or they get realy transparent. + auto node = dynamic_cast(destination); + if (node && node->GetData()) + { + identifiable = dynamic_cast(node->GetData()); + } + } + + return identifiable; +} mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::Connect(IPropertyOwner *source, const IPropertyProvider *destination) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } if (this->IsAbstract()) { mitkThrow() << "Error. This is an abstract property relation rule. Abstract rule must not make a connection. Please use a concrete rule."; } auto instanceIDs = this->GetInstanceID_IDLayer(source, destination); bool hasIDlayer = !instanceIDs.empty(); - auto instanceIDs_data = this->GetInstanceID_datalayer(source, destination); - if (instanceIDs_data.size() > 1) - { - MITK_WARN << "Property relation on data level is ambiguous. First relation is used. Instance ID: " - << instanceIDs_data.front(); - } - bool hasDatalayer = !instanceIDs_data.empty(); + auto relUIDs_data = this->GetRelationUIDs_DataLayer(source, destination, {}); - if (hasIDlayer && hasDatalayer && instanceIDs.front() != instanceIDs_data.front()) + if (relUIDs_data.size() > 1) { - mitkThrow() << "Property relation information is in an invalid state. ID and data layer point to different " - "relation instances. Rule: " - << this->GetRuleID() << "; ID based instance: " << instanceIDs.front() - << "; Data base instance: " << instanceIDs_data.front(); + MITK_WARN << "Property relation on data level is ambiguous. First relation is used. RelationUID ID: " + << relUIDs_data.front().first; } + bool hasDatalayer = !relUIDs_data.empty(); + RelationUIDType relationUID = this->CreateRelationUID(); - InstanceIDType instanceID = ""; + InstanceIDType instanceID = NULL_INSTANCE_ID(); if (hasIDlayer) { instanceID = instanceIDs.front(); } else if (hasDatalayer) { - instanceID = instanceIDs_data.front(); + try + { + instanceID = this->GetInstanceIDByRelationUID(source, relUIDs_data.front().first); + } + catch(...) + { } } - else + + if(instanceID == NULL_INSTANCE_ID()) { instanceID = this->CreateNewRelationInstance(source, relationUID); } auto relUIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("relationUID")); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); auto ruleIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("ruleID")); source->SetProperty(ruleIDKey, mitk::StringProperty::New(this->GetRuleID())); if (!hasIDlayer) { - auto identifiable = dynamic_cast(destination); - - if (!identifiable) - { //This check and pass through to data is needed due to solve T25711. See Task for more information. - //This could be removed at the point we can get rid of DataNodes or they get realy transparent. - auto node = dynamic_cast(destination); - if (node && node->GetData()) - { - identifiable = dynamic_cast(node->GetData()); - } - } + auto identifiable = this->CastProviderAsIdentifiable(destination); if (identifiable) { auto destUIDKey = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID).AddElement("destinationUID")); source->SetProperty(destUIDKey, mitk::StringProperty::New(identifiable->GetUID())); } } - if (!hasDatalayer) - { - this->Connect_datalayer(source, destination, instanceID); - } + this->Connect_datalayer(source, destination, instanceID); return relationUID; -}; +} -void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, const IPropertyProvider *destination) const +void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, const IPropertyProvider *destination, RelationType layer) const { + if (source == nullptr) + { + mitkThrow() << "Error. Source is invalid. Cannot disconnect."; + } + + if (destination == nullptr) + { + mitkThrow() << "Error. Destination is invalid. Cannot disconnect."; + } + try { const auto relationUIDs = this->GetRelationUIDs(source, destination); for (const auto relUID: relationUIDs) { - this->Disconnect(source, relUID); + this->Disconnect(source, relUID, layer); } } catch (const NoPropertyRelationException &) { // nothing to do and no real error in context of disconnect. } -}; +} -void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, RelationUIDType relationUID) const +void mitk::PropertyRelationRuleBase::Disconnect(IPropertyOwner *source, RelationUIDType relationUID, RelationType layer) const { - auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); + if (source == nullptr) + { + mitkThrow() << "Error. Source is invalid. Cannot disconnect."; + } - if (instanceID != NULL_INSTANCE_ID()) + if (layer == RelationType::Data || layer == RelationType::Complete) { - this->Disconnect_datalayer(source, instanceID); + this->Disconnect_datalayer(source, relationUID); + } + auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); + if ((layer == RelationType::ID || layer == RelationType::Complete) && instanceID != NULL_INSTANCE_ID()) + { auto instancePrefix = PropertyKeyPathToPropertyName(GetRootKeyPath().AddElement(instanceID)); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (key.find(instancePrefix) == 0) { source->RemoveProperty(key); } } } -}; +} mitk::PropertyRelationRuleBase::RelationUIDType mitk::PropertyRelationRuleBase::CreateRelationUID() { UIDGenerator generator; return generator.GetUID(); -}; +} /**This mutex is used to guard mitk::PropertyRelationRuleBase::CreateNewRelationInstance by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it is an implementation detail. */ std::mutex relationCreationLock; mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::CreateNewRelationInstance( IPropertyOwner *source, const RelationUIDType &relationUID) const { std::lock_guard guard(relationCreationLock); ////////////////////////////////////// // Get all existing instanc IDs std::vector instanceIDs; InstanceIDType newID = "1"; auto destRegExStr = PropertyKeyPathToPropertyRegEx(this->GetRootKeyPath().AddAnyElement().AddElement("relationUID")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { if (instance_matches.size()>1) { instanceIDs.push_back(std::stoi(instance_matches[1])); } } } ////////////////////////////////////// // Get new ID std::sort(instanceIDs.begin(), instanceIDs.end()); if (!instanceIDs.empty()) { newID = std::to_string(instanceIDs.back() + 1); } ////////////////////////////////////// // reserve new ID auto relUIDKey = PropertyKeyPathToPropertyName(this->GetRootKeyPath().AddElement(newID).AddElement("relationUID")); source->SetProperty(relUIDKey, mitk::StringProperty::New(relationUID)); return newID; -}; +} itk::LightObject::Pointer mitk::PropertyRelationRuleBase::InternalClone() const { return Superclass::InternalClone(); -}; +} mitk::PropertyRelationRuleBase::InstanceIDType mitk::PropertyRelationRuleBase::GetInstanceIDByPropertyName(const std::string propName) { auto proppath = PropertyNameToPropertyKeyPath(propName); auto ref = GetRootKeyPath(); if (proppath.GetSize() < 3 || !(proppath.GetFirstNode() == ref.GetFirstNode()) || !(proppath.GetNode(1) == ref.GetNode(1))) { mitkThrow() << "Property name is not for a RII property or containes no instance ID. Wrong name: " << propName; } return proppath.GetNode(2).name; -}; +} mitk::PropertyRelationRuleBase::RuleIDType mitk::PropertyRelationRuleBase::GetRuleIDByInstanceID(const IPropertyProvider *source, const InstanceIDType &instanceID) const { if (!source) { mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; } auto path = GetRootKeyPath().AddElement(instanceID).AddElement("ruleID"); auto name = PropertyKeyPathToPropertyName(path); const auto prop = source->GetConstProperty(name); std::string result; if (prop.IsNotNull()) { result = prop->GetValueAsString(); } if (result.empty()) { mitkThrowException(NoPropertyRelationException) << "Error. Source has no property relation with the passed instance ID. Instance ID: " << instanceID; } return result; -}; +} + +std::string mitk::PropertyRelationRuleBase::GetDestinationUIDByInstanceID(const IPropertyProvider* source, + const InstanceIDType& instanceID) const +{ + if (!source) + { + mitkThrow() << "Error. Source is invalid. Cannot deduce rule ID"; + } + + auto path = GetRootKeyPath().AddElement(instanceID).AddElement("destinationUID"); + auto name = PropertyKeyPathToPropertyName(path); + + const auto prop = source->GetConstProperty(name); + + std::string result; + + if (prop.IsNotNull()) + { + result = prop->GetValueAsString(); + } + + return result; +} + namespace mitk { /** * \brief Predicate used to wrap rule checks. * * \ingroup DataStorage */ class NodePredicateRuleFunction : public NodePredicateBase { public: using FunctionType = std::function; mitkClassMacro(NodePredicateRuleFunction, NodePredicateBase) mitkNewMacro2Param(NodePredicateRuleFunction, const FunctionType &, PropertyRelationRuleBase::ConstPointer) ~NodePredicateRuleFunction() override = default; bool CheckNode(const mitk::DataNode *node) const override { if (!node) { return false; } return m_Function(node, m_Rule); }; protected: explicit NodePredicateRuleFunction(const FunctionType &function, PropertyRelationRuleBase::ConstPointer rule) : m_Function(function), m_Rule(rule) { }; FunctionType m_Function; PropertyRelationRuleBase::ConstPointer m_Rule; }; } // namespace mitk mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourceCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSourceCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); -}; +} mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationCandidateIndicator() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsDestinationCandidate(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); -}; +} mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetConnectedSourcesDetector() const { auto check = [](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { return rule->IsSource(node); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); -}; +} mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetSourcesDetector( - const IPropertyProvider *destination, RelationType minimalRelation) const + const IPropertyProvider *destination, RelationType exclusiveRelation) const { if (!destination) { mitkThrow() << "Error. Passed destination pointer is NULL"; } - auto check = [destination, minimalRelation](const mitk::IPropertyProvider *node, + auto check = [destination, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { - return rule->HasRelation(node, destination) >= minimalRelation; + return rule->HasRelation(node, destination, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); -}; +} mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationsDetector( - const IPropertyProvider *source, RelationType minimalRelation) const + const IPropertyProvider *source, RelationType exclusiveRelation) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } - auto check = [source, minimalRelation](const mitk::IPropertyProvider *node, + auto check = [source, exclusiveRelation](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { - return rule->HasRelation(source, node) >= minimalRelation; + return rule->HasRelation(source, node, exclusiveRelation); }; return NodePredicateRuleFunction::New(check, this).GetPointer(); -}; +} mitk::NodePredicateBase::ConstPointer mitk::PropertyRelationRuleBase::GetDestinationDetector( const IPropertyProvider *source, RelationUIDType relationUID) const { if (!source) { mitkThrow() << "Error. Passed source pointer is NULL"; } auto relUIDs = this->GetExistingRelations(source); if (std::find(relUIDs.begin(), relUIDs.end(), relationUID) == relUIDs.end()) { mitkThrow() << "Error. Passed relationUID does not identify a relation instance of the passed source for this rule instance."; }; auto check = [source, relationUID](const mitk::IPropertyProvider *node, const mitk::PropertyRelationRuleBase *rule) { try { auto relevantUIDs = rule->GetRelationUIDs(source, node); for (const auto& aUID : relevantUIDs) { if (aUID == relationUID) { return true; } } } catch(const NoPropertyRelationException &) { return false; } return false; }; return NodePredicateRuleFunction::New(check, this).GetPointer(); -}; +} diff --git a/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp b/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp index 175829e218..6d9b58e301 100644 --- a/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp +++ b/Modules/Core/src/DataManagement/mitkSourceImageRelationRule.cpp @@ -1,376 +1,414 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkSourceImageRelationRule.h" #include "mitkPropertyNameHelper.h" #include "mitkStringProperty.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkDataNode.h" +#include "mitkIdentifiable.h" +std::string mitk::SourceImageRelationRule::GenerateRuleID(const std::string& purpose) const +{ + return "SourceImageRelation " + purpose; +} bool mitk::SourceImageRelationRule::IsAbstract() const { return m_PurposeTag.empty(); }; bool mitk::SourceImageRelationRule::IsSupportedRuleID(const RuleIDType& ruleID) const { return ruleID == this->GetRuleID() || (IsAbstract() && ruleID.find("SourceImageRelation ") == 0); }; mitk::SourceImageRelationRule::RuleIDType mitk::SourceImageRelationRule::GetRuleID() const { - return "SourceImageRelation " + m_PurposeTag; + return this->GenerateRuleID(m_PurposeTag); }; std::string mitk::SourceImageRelationRule::GetDisplayName() const { return m_DisplayName; }; std::string mitk::SourceImageRelationRule::GetSourceRoleName() const { return m_SourceRole; }; std::string mitk::SourceImageRelationRule::GetDestinationRoleName() const { return m_DestinationRole; }; bool mitk::SourceImageRelationRule::IsDestinationCandidate(const IPropertyProvider *owner) const { auto node = dynamic_cast(owner); auto image = nullptr != node ? dynamic_cast(node->GetData()) : dynamic_cast(owner); return image != nullptr; } mitk::SourceImageRelationRule::RelationUIDType mitk::SourceImageRelationRule::Connect(Image *source, const Image *destination) const { return Superclass::Connect(source, destination); }; mitk::SourceImageRelationRule::SourceImageRelationRule() : m_PurposeTag(""), m_DisplayName("Abstract image to image relation"), m_SourceRole("derived data"), m_DestinationRole("source image") {}; mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag) : SourceImageRelationRule(purposeTag, purposeTag + " relation"){}; mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName) : SourceImageRelationRule( purposeTag, displayName, "derived data", "source image"){}; mitk::SourceImageRelationRule::SourceImageRelationRule(const RuleIDType &purposeTag, const std::string &displayName, const std::string &sourceRole, const std::string &destinationRole) : m_PurposeTag(purposeTag), m_DisplayName(displayName), m_SourceRole(sourceRole), m_DestinationRole(destinationRole){}; -mitk::SourceImageRelationRule::InstanceIDVectorType mitk::SourceImageRelationRule::GetInstanceID_datalayer( - const IPropertyProvider * source, const IPropertyProvider * destination) const +mitk::SourceImageRelationRule::DataRelationUIDVectorType +mitk::SourceImageRelationRule::GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const { - InstanceIDVectorType result; + DataRelationUIDVectorType result; - auto relevantReferenceIndices = GetReferenceSequenceIndices(source, destination); + auto relevantIndicesAndRuleIDs = GetReferenceSequenceIndices(source, destination, instances_IDLayer); - auto itemRegExStr = this->GetRIIPropertyRegEx("SourceImageSequenceItem"); - auto regEx = std::regex(itemRegExStr); + auto itemRIIRegExStr = this->GetRIIPropertyRegEx("SourceImageSequenceItem"); + auto regEx = std::regex(itemRIIRegExStr); //workaround until T24729 is done. Please remove if T24728 is done auto keys = PropertyRelationRuleBase::GetPropertyKeys(source); //end workaround for T24729 - for (const auto &key : keys) + for (const auto &indexNRule : relevantIndicesAndRuleIDs) { - if (std::regex_match(key, regEx)) + bool relationCoveredByRII = false; + for (const auto& key : keys) { - auto sequItemProp = source->GetConstProperty(key); - if (sequItemProp.IsNotNull()) + if (std::regex_match(key, regEx)) { - auto finding = std::find(std::cbegin(relevantReferenceIndices), std::cend(relevantReferenceIndices), sequItemProp->GetValueAsString()); - if (finding != std::cend(relevantReferenceIndices)) + auto sequItemProp = source->GetConstProperty(key); + if (sequItemProp.IsNotNull() && sequItemProp->GetValueAsString() == std::to_string(indexNRule.first)) { + relationCoveredByRII = true; auto instanceID = GetInstanceIDByPropertyName(key); auto ruleID = GetRuleIDByInstanceID(source, instanceID); if (this->IsSupportedRuleID(ruleID)) { - result.push_back(instanceID); + result.emplace_back(this->GetRelationUIDByInstanceID(source, instanceID), ruleID); } } } } + + if (!relationCoveredByRII) + { + //the relation is not covered on the RII level, so we generate the property path to the DICOM source reference (this is done via + //the SOP Instance UIDs which uniquely identify an DICOM IOD). We use this property path as relation UID because it is identifying + //on the data level (even so not long term stable if relations with a lower index are removed). + PropertyKeyPath referencedInstanceUIDs; + referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddSelection("2112", indexNRule.first).AddElement("0008").AddElement("1155"); + + std::string ruleID = ""; + if (!indexNRule.second.empty()) + { + ruleID = this->GenerateRuleID(indexNRule.second); + } + + result.emplace_back(PropertyKeyPathToPropertyName(referencedInstanceUIDs), ruleID); + } } return result; }; -std::vector mitk::SourceImageRelationRule::GetReferenceSequenceIndices(const IPropertyProvider * source, - const IPropertyProvider * destination) const +std::vector > mitk::SourceImageRelationRule::GetReferenceSequenceIndices(const IPropertyProvider * source, + const IPropertyProvider * destination, InstanceIDVectorType ignoreInstances) const { - std::vector result; + std::vector > result; BaseProperty::ConstPointer destInstanceUIDProp; + std::string destinationUID = ""; if (destination) { destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); if (destInstanceUIDProp.IsNull()) { return result; } + + auto identifiable = this->CastProviderAsIdentifiable(destination); + + if (identifiable) + { + destinationUID= identifiable->GetUID(); + } + } + + std::vector ignoreItemIndices; + for (const auto& iID : ignoreInstances) + { + auto sourceImageRefPath = GetRootKeyPath().AddElement(iID).AddElement("SourceImageSequenceItem"); + auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath)); + + if (imageRefProp.IsNotNull()) + { + ignoreItemIndices.emplace_back(imageRefProp->GetValueAsString()); + } } PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); - auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);; + auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs); auto regEx = std::regex(sourceRegExStr); std::vector keys; //workaround until T24729 is done. Please remove if T24728 is done keys = PropertyRelationRuleBase::GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto refUIDProp = source->GetConstProperty(key); if (destination==nullptr || *refUIDProp == *destInstanceUIDProp) { auto currentKeyPath = PropertyNameToPropertyKeyPath(key); auto currentKeyPathSelection = currentKeyPath.GetNode(2).selection; - PropertyKeyPath purposePath; - purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", currentKeyPathSelection).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); - auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); - if (this->IsAbstract() || (purposeProp.IsNotNull() && purposeProp->GetValueAsString() == this->m_PurposeTag)) + auto finding = std::find(ignoreItemIndices.begin(), ignoreItemIndices.end(), std::to_string(currentKeyPathSelection)); + if (finding == ignoreItemIndices.end()) { - result.push_back(std::to_string(currentKeyPathSelection)); + PropertyKeyPath purposePath; + purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", currentKeyPathSelection).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); + auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); + std::string currentPurpose = ""; + if (purposeProp.IsNotNull()) + { + currentPurpose = purposeProp->GetValueAsString(); + } + if (this->IsAbstract() || (purposeProp.IsNotNull() && currentPurpose == this->m_PurposeTag)) + { + result.emplace_back(currentKeyPathSelection, currentPurpose); + } } } } } return result; }; -bool mitk::SourceImageRelationRule::HasImplicitDataRelation(const IPropertyProvider * source, - const IPropertyProvider * destination) const -{ - auto relevantReferences = GetReferenceSequenceIndices(source, destination); - - if (this->IsAbstract()) - { - return !relevantReferences.empty(); - } - else - { - for (auto referenceIndex : relevantReferences) - { - PropertyKeyPath purposePath; - purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", std::stoi(referenceIndex)).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); - auto purposeProp = source->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); - - if (purposeProp.IsNotNull() && purposeProp->GetValueAsString() == this->m_PurposeTag) - { - return true; - } - } - } - - return false; -}; - - /**This mutex is used to guard mitk::SourceImageRelationRule::CreateNewSourceImageSequenceItem by a class wide mutex to avoid racing conditions in a scenario where rules are used concurrently. It is not in the class interface itself, because it is an implementation detail. */ namespace { std::mutex sequenceItemCreationLock; } mitk::PropertyKeyPath::ItemSelectionIndex mitk::SourceImageRelationRule::CreateNewSourceImageSequenceItem( IPropertyOwner *source) const { std::lock_guard guard(sequenceItemCreationLock); ////////////////////////////////////// // Get all existing sequence items std::vector instanceIDs; PropertyKeyPath::ItemSelectionIndex newID = 0; PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); auto regExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs); auto regEx = std::regex(regExStr); std::smatch instance_matches; //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { if (instance_matches.size()>1) { instanceIDs.push_back(std::stoi(instance_matches[1])); } } } ////////////////////////////////////// // Get new ID std::sort(instanceIDs.begin(), instanceIDs.end()); if (!instanceIDs.empty()) { newID = instanceIDs.back()+1; } ////////////////////////////////////// // reserve new ID PropertyKeyPath newSourceImageSequencePath; newSourceImageSequencePath.AddElement("DICOM").AddElement("0008").AddSelection("2112",newID).AddElement("0008").AddElement("1155"); auto newKey = PropertyKeyPathToPropertyName(newSourceImageSequencePath); source->SetProperty(newKey, mitk::TemporoSpatialStringProperty::New("reserved slot for source image sequence")); return newID; }; void mitk::SourceImageRelationRule::Connect_datalayer(IPropertyOwner * source, const IPropertyProvider * destination, const InstanceIDType & instanceID) const { auto destInstanceUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008,0x0018)); auto destClassUIDProp = destination->GetConstProperty(GeneratePropertyNameForDICOMTag(0x0008, 0x0016)); if (destInstanceUIDProp.IsNotNull() && destClassUIDProp.IsNotNull()) { auto existingRefs = this->GetReferenceSequenceIndices(source, destination); std::string newSelectionIndexStr; if (!existingRefs.empty()) { - newSelectionIndexStr = existingRefs[0]; + newSelectionIndexStr = std::to_string(existingRefs[0].first); } else { PropertyKeyPath::ItemSelectionIndex newSelectionIndex = CreateNewSourceImageSequenceItem(source); PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1155"); source->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), destInstanceUIDProp->Clone()); PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0008").AddElement("1150"); source->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), destClassUIDProp->Clone()); PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", newSelectionIndex).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); source->SetProperty(PropertyKeyPathToPropertyName(purposePath), StringProperty::New(m_PurposeTag)); newSelectionIndexStr = std::to_string(newSelectionIndex); } auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); source->SetProperty(PropertyKeyPathToPropertyName(sourceImageRefPath), StringProperty::New(newSelectionIndexStr).GetPointer()); } else { MITK_DEBUG << "Cannot connect SourceImageRelationRule on data layer. Passed destination does not have properties for DICOM SOP Instance UIDs(0x0008, 0x0018) and DICOM SOP Class UID(0x0008, 0x0016)"; } }; -void mitk::SourceImageRelationRule::Disconnect_datalayer(IPropertyOwner * source, const InstanceIDType & instanceID) const +void mitk::SourceImageRelationRule::Disconnect_datalayer(IPropertyOwner * source, const RelationUIDType & relationUID) const { - auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); - auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath)); + std::string deletedImageRefSequenceIndexStr = ""; - if (imageRefProp.IsNotNull()) + if (relationUID.find("DICOM") == 0) + { //relation that is purly data based. + auto currentKeyPath = PropertyNameToPropertyKeyPath(relationUID); + deletedImageRefSequenceIndexStr = std::to_string(currentKeyPath.GetNode(2).selection); + } + else { - auto deletedImageRefSequenceIndex = imageRefProp->GetValueAsString(); + auto sourceImageRefPath = GetRootKeyPath().AddElement(this->GetInstanceIDByRelationUID(source,relationUID)).AddElement("SourceImageSequenceItem"); + auto imageRefProp = source->GetConstProperty(PropertyKeyPathToPropertyName(sourceImageRefPath)); + if (imageRefProp.IsNotNull()) + { + deletedImageRefSequenceIndexStr = imageRefProp->GetValueAsString(); + } + } + if(!deletedImageRefSequenceIndexStr.empty()) + { + auto deletedImageRefSequenceIndex = std::stoull(deletedImageRefSequenceIndexStr); auto refs = GetReferenceSequenceIndices(source); std::sort(refs.begin(), refs.end()); - for (auto refIndexStr : refs) + for (auto refIndex : refs) { - auto refIndex = std::stoi(refIndexStr); - - if (refIndex >= std::stoi(deletedImageRefSequenceIndex)) + if (refIndex.first >= deletedImageRefSequenceIndex) { PropertyKeyPath refDICOMDataPath; - refDICOMDataPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", refIndex); + refDICOMDataPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", refIndex.first); auto prefix = PropertyKeyPathToPropertyName(refDICOMDataPath); PropertyKeyPath refRelDataPath = GetRootKeyPath().AddAnyElement().AddElement("SourceImageSequenceItem");; - auto regEx = std::regex(PropertyKeyPathToPropertyRegEx(refRelDataPath)); + auto riiRegEx = std::regex(PropertyKeyPathToPropertyRegEx(refRelDataPath)); //workaround until T24729 is done. You can use directly source->GetPropertyKeys again, when fixed. const auto keys = GetPropertyKeys(source); //end workaround for T24729 for (const auto &key : keys) { if (key.find(prefix) == 0) { //its a relevant DICOM property delete or update - if (refIndexStr != deletedImageRefSequenceIndex) + if (refIndex.first != deletedImageRefSequenceIndex) { //reindex to close the gap in the dicom sequence. auto newPath = PropertyNameToPropertyKeyPath(key); - newPath.GetNode(2).selection = refIndex - 1; + newPath.GetNode(2).selection = refIndex.first - 1; source->SetProperty(PropertyKeyPathToPropertyName(newPath), source->GetNonConstProperty(key)); } //remove old/outdated data layer information source->RemoveProperty(key); - - auto sourceImageRefPath = GetRootKeyPath().AddElement(instanceID).AddElement("SourceImageSequenceItem"); } - if (std::regex_match(key, regEx)) - { + if (std::regex_match(key, riiRegEx)) + { //it is a relevant RII property, remove it or update it. auto imageSequenceItemProp = source->GetConstProperty(key); - if (imageSequenceItemProp->GetValueAsString() == std::to_string(refIndex)) + if (imageSequenceItemProp->GetValueAsString() == deletedImageRefSequenceIndexStr) + { + source->RemoveProperty(key); + } + else if (imageSequenceItemProp->GetValueAsString() == std::to_string(refIndex.first)) { - //its a relevant data property of the relation rule. - source->SetProperty(key, StringProperty::New(std::to_string(refIndex - 1))); + //its a relevant data property of the relation rule reindex it. + source->SetProperty(key, StringProperty::New(std::to_string(refIndex.first - 1))); } } } } } } }; itk::LightObject::Pointer mitk::SourceImageRelationRule::InternalClone() const { itk::LightObject::Pointer result = Self::New(this->m_PurposeTag, this->m_DisplayName, this->m_SourceRole, this->m_DestinationRole).GetPointer(); return result; }; diff --git a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp index a75dfc2494..845d8bd4d3 100644 --- a/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp +++ b/Modules/Core/src/DataManagement/mitkTemporoSpatialStringProperty.cpp @@ -1,364 +1,499 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTemporoSpatialStringProperty.h" -#ifdef _MSC_VER -// has to be deactivated because of a bug in boost v1.59. see Boost bug ticket #11599 -// as soon as MITK uses a boost version with a bug fix we can remove the disableling. -#pragma warning(push) -#pragma warning(disable : 4715) -#endif - #include #include #include mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const char *s) { if (s) { SliceMapType slices{{0, s}}; m_Values.insert(std::make_pair(0, slices)); } } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const std::string &s) { SliceMapType slices{{0, s}}; m_Values.insert(std::make_pair(0, slices)); } mitk::TemporoSpatialStringProperty::TemporoSpatialStringProperty(const TemporoSpatialStringProperty &other) : BaseProperty(other), m_Values(other.m_Values) { } bool mitk::TemporoSpatialStringProperty::IsEqual(const BaseProperty &property) const { return this->m_Values == static_cast(property).m_Values; } bool mitk::TemporoSpatialStringProperty::Assign(const BaseProperty &property) { this->m_Values = static_cast(property).m_Values; return true; } std::string mitk::TemporoSpatialStringProperty::GetValueAsString() const { return GetValue(); } +bool mitk::TemporoSpatialStringProperty::IsUniform() const +{ + auto refValue = this->GetValue(); + + for (const auto& timeStep : m_Values) + { + auto finding = std::find_if_not(timeStep.second.begin(), timeStep.second.end(), [&refValue](const mitk::TemporoSpatialStringProperty::SliceMapType::value_type& val) { return val.second == refValue; }); + if (finding != timeStep.second.end()) + { + return false; + } + } + + return true; +} + itk::LightObject::Pointer mitk::TemporoSpatialStringProperty::InternalClone() const { itk::LightObject::Pointer result(new Self(*this)); result->UnRegister(); return result; } mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue() const { std::string result = ""; if (!m_Values.empty()) { if (!m_Values.begin()->second.empty()) { result = m_Values.begin()->second.begin()->second; } } return result; }; std::pair mitk::TemporoSpatialStringProperty::CheckValue( const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { std::string value = ""; bool found = false; auto timeIter = m_Values.find(timeStep); auto timeEnd = m_Values.end(); if (timeIter == timeEnd && allowCloseTime) { // search for closest time step (earlier preverd) timeIter = m_Values.upper_bound(timeStep); if (timeIter != m_Values.begin()) { // there is a key lower than time step timeIter = std::prev(timeIter); } } if (timeIter != timeEnd) { const SliceMapType &slices = timeIter->second; auto sliceIter = slices.find(zSlice); auto sliceEnd = slices.end(); if (sliceIter == sliceEnd && allowCloseSlice) { // search for closest slice (earlier preverd) sliceIter = slices.upper_bound(zSlice); if (sliceIter != slices.begin()) { // there is a key lower than slice sliceIter = std::prev(sliceIter); } } if (sliceIter != sliceEnd) { value = sliceIter->second; found = true; } } return std::make_pair(found, value); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).second; }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueBySlice( const IndexValueType &zSlice, bool allowClose) const { return GetValue(0, zSlice, true, allowClose); }; mitk::TemporoSpatialStringProperty::ValueType mitk::TemporoSpatialStringProperty::GetValueByTimeStep( const TimeStepType &timeStep, bool allowClose) const { return GetValue(timeStep, 0, allowClose, true); }; bool mitk::TemporoSpatialStringProperty::HasValue() const { return !m_Values.empty(); }; bool mitk::TemporoSpatialStringProperty::HasValue(const TimeStepType &timeStep, const IndexValueType &zSlice, bool allowCloseTime, bool allowCloseSlice) const { return CheckValue(timeStep, zSlice, allowCloseTime, allowCloseSlice).first; }; bool mitk::TemporoSpatialStringProperty::HasValueBySlice(const IndexValueType &zSlice, bool allowClose) const { return HasValue(0, zSlice, true, allowClose); }; bool mitk::TemporoSpatialStringProperty::HasValueByTimeStep(const TimeStepType &timeStep, bool allowClose) const { return HasValue(timeStep, 0, allowClose, true); }; +std::vector mitk::TemporoSpatialStringProperty::GetAvailableSlices() const +{ + std::set uniqueSlices; + + for (const auto& timeStep : m_Values) + { + for (const auto& slice : timeStep.second) + { + uniqueSlices.insert(slice.first); + } + } + + return std::vector(std::begin(uniqueSlices), std::end(uniqueSlices)); +} + std::vector mitk::TemporoSpatialStringProperty::GetAvailableSlices( const TimeStepType &timeStep) const { std::vector result; auto timeIter = m_Values.find(timeStep); auto timeEnd = m_Values.end(); if (timeIter != timeEnd) { for (auto const &element : timeIter->second) { result.push_back(element.first); } } return result; }; std::vector mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps() const { std::vector result; for (auto const &element : m_Values) { result.push_back(element.first); } return result; }; +std::vector mitk::TemporoSpatialStringProperty::GetAvailableTimeSteps(const IndexValueType& slice) const +{ + std::vector result; + + for (const auto& timeStep : m_Values) + { + if (timeStep.second.find(slice) != std::end(timeStep.second)) + { + result.push_back(timeStep.first); + } + } + return result; +} + + void mitk::TemporoSpatialStringProperty::SetValue(const TimeStepType &timeStep, const IndexValueType &zSlice, const ValueType &value) { auto timeIter = m_Values.find(timeStep); auto timeEnd = m_Values.end(); if (timeIter == timeEnd) { SliceMapType slices{{zSlice, value}}; m_Values.insert(std::make_pair(timeStep, slices)); } else { timeIter->second[zSlice] = value; } this->Modified(); }; void mitk::TemporoSpatialStringProperty::SetValue(const ValueType &value) { this->Modified(); m_Values.clear(); this->SetValue(0, 0, value); }; // Create necessary escape sequences from illegal characters // REMARK: This code is based upon code from boost::ptree::json_writer. // The corresponding boost function was not used directly, because it is not part of // the public interface of ptree::json_writer. :( // A own serialization strategy was implemented instead of using boost::ptree::json_write because // currently (<= boost 1.60) everything (even numbers) are converted into string representations // by the writer, so e.g. it becomes "t":"2" instaed of "t":2 template std::basic_string CreateJSONEscapes(const std::basic_string &s) { std::basic_string result; typename std::basic_string::const_iterator b = s.begin(); typename std::basic_string::const_iterator e = s.end(); while (b != e) { typedef typename boost::make_unsigned::type UCh; UCh c(*b); // This assumes an ASCII superset. // We escape everything outside ASCII, because this code can't // handle high unicode characters. if (c == 0x20 || c == 0x21 || (c >= 0x23 && c <= 0x2E) || (c >= 0x30 && c <= 0x5B) || (c >= 0x5D && c <= 0x7F)) result += *b; else if (*b == Ch('\b')) result += Ch('\\'), result += Ch('b'); else if (*b == Ch('\f')) result += Ch('\\'), result += Ch('f'); else if (*b == Ch('\n')) result += Ch('\\'), result += Ch('n'); else if (*b == Ch('\r')) result += Ch('\\'), result += Ch('r'); else if (*b == Ch('\t')) result += Ch('\\'), result += Ch('t'); else if (*b == Ch('/')) result += Ch('\\'), result += Ch('/'); else if (*b == Ch('"')) result += Ch('\\'), result += Ch('"'); else if (*b == Ch('\\')) result += Ch('\\'), result += Ch('\\'); else { const char *hexdigits = "0123456789ABCDEF"; unsigned long u = (std::min)(static_cast(static_cast(*b)), 0xFFFFul); int d1 = u / 4096; u -= d1 * 4096; int d2 = u / 256; u -= d2 * 256; int d3 = u / 16; u -= d3 * 16; int d4 = u; result += Ch('\\'); result += Ch('u'); result += Ch(hexdigits[d1]); result += Ch(hexdigits[d2]); result += Ch(hexdigits[d3]); result += Ch(hexdigits[d4]); } ++b; } return result; } +using CondensedTimeKeyType = std::pair; +using CondensedTimePointsType = std::map; + +using CondensedSliceKeyType = std::pair; +using CondensedSlicesType = std::map; + +/** Helper function that checks if between an ID and a successing ID is no gap.*/ +template +bool isGap(const TValue& value, const TValue& successor) +{ + return value successor + 1; +} + + +template +void CheckAndCondenseElement(const TNewKey& newKeyMinID, const TNewValue& newValue, TMasterKey& masterKey, TMasterValue& masterValue, TCondensedContainer& condensedContainer) +{ + if (newValue != masterValue + || isGap(newKeyMinID, masterKey.second)) + { + condensedContainer[masterKey] = masterValue; + masterValue = newValue; + masterKey.first = newKeyMinID; + } + masterKey.second = newKeyMinID; +} + +/** Helper function that tries to condense the values of time points for a slice as much as possible and returns all slices with condensed timepoint values.*/ +CondensedSlicesType CondenseTimePointValuesOfProperty(const mitk::TemporoSpatialStringProperty* tsProp) +{ + CondensedSlicesType uncondensedSlices; + + auto zs = tsProp->GetAvailableSlices(); + for (const auto z : zs) + { + CondensedTimePointsType condensedTimePoints; + auto timePointIDs = tsProp->GetAvailableTimeSteps(z); + CondensedTimeKeyType condensedKey = { timePointIDs.front(),timePointIDs.front() }; + auto refValue = tsProp->GetValue(timePointIDs.front(), z); + + for (const auto timePointID : timePointIDs) + { + const auto& newVal = tsProp->GetValue(timePointID, z); + CheckAndCondenseElement(timePointID, newVal, condensedKey, refValue, condensedTimePoints); + } + condensedTimePoints[condensedKey] = refValue; + uncondensedSlices[{ z, z }] = condensedTimePoints; + } + return uncondensedSlices; +} + ::std::string mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON( const mitk::BaseProperty *prop) { // REMARK: Implemented own serialization instead of using boost::ptree::json_write because // currently (<= boost 1.60) everything (even numbers) are converted into string representations // by the writer, so e.g. it becomes "t":"2" instaed of "t":2 // If this problem is fixed with boost, we shoud switch back to json_writer (and remove the custom // implementation of CreateJSONEscapes (see above)). const auto *tsProp = dynamic_cast(prop); if (!tsProp) { mitkThrow() << "Cannot serialize properties of types other than TemporoSpatialStringProperty."; } std::ostringstream stream; + stream.imbue(std::locale("C")); stream << "{\"values\":["; - std::vector ts = tsProp->GetAvailableTimeSteps(); - bool first = true; - for (auto t : ts) + //we condense the content of the property to have a compact serialization. + //we start with condensing time points and then slices (in difference to the + //internal layout). Reason: There is more entropy in slices (looking at DICOM) + //than across time points for one slice, so we can "compress" to a higher rate. + //We don't wanted to change the internal structure of the property as it would + //introduce API inconvinience and subtle changes in behavior. + CondensedSlicesType uncondensedSlices = CondenseTimePointValuesOfProperty(tsProp); + + //now condense the slices + CondensedSlicesType condensedSlices; + if(!uncondensedSlices.empty()) { - std::vector zs = tsProp->GetAvailableSlices(t); - for (auto z : zs) + CondensedTimePointsType& masterSlice = uncondensedSlices.begin()->second; + CondensedSliceKeyType masterSliceKey = uncondensedSlices.begin()->first; + + for (const auto& uncondensedSlice : uncondensedSlices) { - std::string value = CreateJSONEscapes(tsProp->GetValue(t, z)); + const auto& uncondensedSliceID = uncondensedSlice.first.first; + CheckAndCondenseElement(uncondensedSliceID, uncondensedSlice.second, masterSliceKey, masterSlice, condensedSlices); + } + condensedSlices[masterSliceKey] = masterSlice; + } + + bool first = true; + for (const auto& z : condensedSlices) + { + for (const auto& t : z.second) + { if (first) { first = false; } else { stream << ", "; } - stream << "{\"t\":" << t << ", \"z\":" << z << ", \"value\":\"" << value << "\"}"; + const auto& minSliceID = z.first.first; + const auto& maxSliceID = z.first.second; + const auto& minTimePointID = t.first.first; + const auto& maxTimePointID = t.first.second; + + stream << "{\"z\":" << minSliceID << ", "; + if (minSliceID != maxSliceID) + { + stream << "\"zmax\":" << maxSliceID << ", "; + } + stream << "\"t\":" << minTimePointID << ", "; + if (minTimePointID != maxTimePointID) + { + stream << "\"tmax\":" << maxTimePointID << ", "; + } + + const auto& value = t.second; + stream << "\"value\":\"" << CreateJSONEscapes(value) << "\"}"; } } stream << "]}"; return stream.str(); } mitk::BaseProperty::Pointer mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty( const std::string &value) { if (value.empty()) return nullptr; mitk::TemporoSpatialStringProperty::Pointer prop = mitk::TemporoSpatialStringProperty::New(); boost::property_tree::ptree root; std::istringstream stream(value); + stream.imbue(std::locale("C")); boost::property_tree::read_json(stream, root); for (boost::property_tree::ptree::value_type &element : root.get_child("values")) { std::string value = element.second.get("value", ""); mitk::TemporoSpatialStringProperty::IndexValueType z = element.second.get("z", 0); + mitk::TemporoSpatialStringProperty::IndexValueType zmax = + element.second.get("zmax", z); TimeStepType t = element.second.get("t", 0); + TimeStepType tmax = element.second.get("tmax", t); - prop->SetValue(t, z, value); + for (auto currentT = t; currentT <= tmax; ++currentT) + { + for (auto currentZ = z; currentZ <= zmax; ++currentZ) + { + prop->SetValue(currentT, currentZ, value); + } + } } return prop.GetPointer(); } - -#ifdef _MSC_VER -#pragma warning(pop) -#endif diff --git a/Modules/Core/src/IO/mitkAbstractFileReader.cpp b/Modules/Core/src/IO/mitkAbstractFileReader.cpp index 05d7b1b837..af38ca14ad 100644 --- a/Modules/Core/src/IO/mitkAbstractFileReader.cpp +++ b/Modules/Core/src/IO/mitkAbstractFileReader.cpp @@ -1,320 +1,332 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include -#include #include +#include +#include #include #include #include #include #include namespace mitk { AbstractFileReader::InputStream::InputStream(IFileReader *reader, std::ios_base::openmode mode) : std::istream(nullptr), m_Stream(nullptr) { std::istream *stream = reader->GetInputStream(); if (stream) { this->init(stream->rdbuf()); } else { m_Stream = new std::ifstream(reader->GetInputLocation().c_str(), mode); this->init(m_Stream->rdbuf()); } } AbstractFileReader::InputStream::~InputStream() { delete m_Stream; } class AbstractFileReader::Impl : public FileReaderWriterBase { public: Impl() : FileReaderWriterBase(), m_Stream(nullptr), m_PrototypeFactory(nullptr) {} Impl(const Impl &other) : FileReaderWriterBase(other), m_Stream(nullptr), m_PrototypeFactory(nullptr) {} std::string m_Location; std::string m_TmpFile; std::istream *m_Stream; us::PrototypeServiceFactory *m_PrototypeFactory; us::ServiceRegistration m_Reg; }; AbstractFileReader::AbstractFileReader() : d(new Impl) {} AbstractFileReader::~AbstractFileReader() { UnregisterService(); delete d->m_PrototypeFactory; if (!d->m_TmpFile.empty()) { std::remove(d->m_TmpFile.c_str()); } } AbstractFileReader::AbstractFileReader(const AbstractFileReader &other) : IFileReader(), d(new Impl(*other.d.get())) { } AbstractFileReader::AbstractFileReader(const CustomMimeType &mimeType, const std::string &description) : d(new Impl) { d->SetMimeType(mimeType); d->SetDescription(description); } ////////////////////// Reading ///////////////////////// std::vector AbstractFileReader::Read() { - std::vector result; - - DataStorage::Pointer ds = StandaloneDataStorage::New().GetPointer(); - this->Read(*ds); - DataStorage::SetOfObjects::ConstPointer dataNodes = ds->GetAll(); - for (DataStorage::SetOfObjects::ConstIterator iter = dataNodes->Begin(), iterEnd = dataNodes->End(); - iter != iterEnd; - ++iter) + std::vector result = this->DoRead(); + + const auto options = this->GetOptions(); + + for (auto& data : result) { - result.push_back(iter.Value()->GetData()); + data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_DESCRIPTION()), StringProperty::New(d->GetDescription())); + data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_VERSION()), StringProperty::New(MITK_VERSION_STRING)); + data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_MIME_NAME()), StringProperty::New(d->GetMimeType()->GetName())); + data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_MIME_CATEGORY()), StringProperty::New(d->GetMimeType()->GetCategory())); + if (this->GetInputStream() == nullptr) + { + data->SetProperty(PropertyKeyPathToPropertyName(IOMetaInformationPropertyConstants::READER_INPUTLOCATION()), StringProperty::New(this->GetInputLocation())); + } + + for (const auto& option : options) + { + auto optionpath = IOMetaInformationPropertyConstants::READER_OPTION_ROOT().AddElement(option.first); + data->SetProperty(PropertyKeyPathToPropertyName(optionpath), StringProperty::New(option.second.ToString())); + } } + return result; } DataStorage::SetOfObjects::Pointer AbstractFileReader::Read(DataStorage &ds) { DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New(); std::vector data = this->Read(); for (auto iter = data.begin(); iter != data.end(); ++iter) { mitk::DataNode::Pointer node = mitk::DataNode::New(); node->SetData(*iter); this->SetDefaultDataNodeProperties(node, this->GetInputLocation()); ds.Add(node); result->InsertElement(result->Size(), node); } return result; } IFileReader::ConfidenceLevel AbstractFileReader::GetConfidenceLevel() const { if (d->m_Stream) { if (*d->m_Stream) return Supported; } else { if (itksys::SystemTools::FileExists(this->GetInputLocation().c_str(), true)) { return Supported; } } return Unsupported; } //////////// µS Registration & Properties ////////////// us::ServiceRegistration AbstractFileReader::RegisterService(us::ModuleContext *context) { if (d->m_PrototypeFactory) return us::ServiceRegistration(); if (context == nullptr) { context = us::GetModuleContext(); } d->RegisterMimeType(context); if (this->GetMimeType()->GetName().empty()) { MITK_WARN << "Not registering reader due to empty MIME type."; return us::ServiceRegistration(); } struct PrototypeFactory : public us::PrototypeServiceFactory { AbstractFileReader *const m_Prototype; PrototypeFactory(AbstractFileReader *prototype) : m_Prototype(prototype) {} us::InterfaceMap GetService(us::Module * /*module*/, const us::ServiceRegistrationBase & /*registration*/) override { return us::MakeInterfaceMap(m_Prototype->Clone()); } void UngetService(us::Module * /*module*/, const us::ServiceRegistrationBase & /*registration*/, const us::InterfaceMap &service) override { delete us::ExtractInterface(service); } }; d->m_PrototypeFactory = new PrototypeFactory(this); us::ServiceProperties props = this->GetServiceProperties(); d->m_Reg = context->RegisterService(d->m_PrototypeFactory, props); return d->m_Reg; } void AbstractFileReader::UnregisterService() { try { d->m_Reg.Unregister(); } catch (const std::exception &) { } } us::ServiceProperties AbstractFileReader::GetServiceProperties() const { us::ServiceProperties result; result[IFileReader::PROP_DESCRIPTION()] = this->GetDescription(); result[IFileReader::PROP_MIMETYPE()] = this->GetMimeType()->GetName(); result[us::ServiceConstants::SERVICE_RANKING()] = this->GetRanking(); return result; } us::ServiceRegistration AbstractFileReader::RegisterMimeType(us::ModuleContext *context) { return d->RegisterMimeType(context); } std::vector< std::string > AbstractFileReader::GetReadFiles(){ return m_ReadFiles; } void AbstractFileReader::SetMimeType(const CustomMimeType &mimeType) { d->SetMimeType(mimeType); } void AbstractFileReader::SetDescription(const std::string &description) { d->SetDescription(description); } void AbstractFileReader::SetRanking(int ranking) { d->SetRanking(ranking); } int AbstractFileReader::GetRanking() const { return d->GetRanking(); } std::string AbstractFileReader::GetLocalFileName() const { std::string localFileName; if (d->m_Stream) { if (d->m_TmpFile.empty()) { // write the stream contents to temporary file std::string ext = itksys::SystemTools::GetFilenameExtension(this->GetInputLocation()); std::ofstream tmpStream; localFileName = mitk::IOUtil::CreateTemporaryFile( tmpStream, std::ios_base::out | std::ios_base::trunc | std::ios_base::binary, "XXXXXX" + ext); tmpStream << d->m_Stream->rdbuf(); d->m_TmpFile = localFileName; } else { localFileName = d->m_TmpFile; } } else { localFileName = d->m_Location; } return localFileName; } //////////////////////// Options /////////////////////// void AbstractFileReader::SetDefaultOptions(const IFileReader::Options &defaultOptions) { d->SetDefaultOptions(defaultOptions); } IFileReader::Options AbstractFileReader::GetDefaultOptions() const { return d->GetDefaultOptions(); } void AbstractFileReader::SetInput(const std::string &location) { d->m_Location = location; d->m_Stream = nullptr; } void AbstractFileReader::SetInput(const std::string &location, std::istream *is) { if (d->m_Stream != is && !d->m_TmpFile.empty()) { std::remove(d->m_TmpFile.c_str()); d->m_TmpFile.clear(); } d->m_Location = location; d->m_Stream = is; } std::string AbstractFileReader::GetInputLocation() const { return d->m_Location; } std::istream *AbstractFileReader::GetInputStream() const { return d->m_Stream; } MimeType AbstractFileReader::GetRegisteredMimeType() const { return d->GetRegisteredMimeType(); } IFileReader::Options AbstractFileReader::GetOptions() const { return d->GetOptions(); } us::Any AbstractFileReader::GetOption(const std::string &name) const { return d->GetOption(name); } void AbstractFileReader::SetOptions(const Options &options) { d->SetOptions(options); } void AbstractFileReader::SetOption(const std::string &name, const us::Any &value) { d->SetOption(name, value); } ////////////////// MISC ////////////////// void AbstractFileReader::AddProgressCallback(const ProgressCallback &callback) { d->AddProgressCallback(callback); } void AbstractFileReader::RemoveProgressCallback(const ProgressCallback &callback) { d->RemoveProgressCallback(callback); } ////////////////// µS related Getters ////////////////// const CustomMimeType *AbstractFileReader::GetMimeType() const { return d->GetMimeType(); } void AbstractFileReader::SetMimeTypePrefix(const std::string &prefix) { d->SetMimeTypePrefix(prefix); } std::string AbstractFileReader::GetMimeTypePrefix() const { return d->GetMimeTypePrefix(); } std::string AbstractFileReader::GetDescription() const { return d->GetDescription(); } void AbstractFileReader::SetDefaultDataNodeProperties(DataNode *node, const std::string &filePath) { // path if (!filePath.empty()) { mitk::StringProperty::Pointer pathProp = mitk::StringProperty::New(itksys::SystemTools::GetFilenamePath(filePath)); node->SetProperty(StringProperty::PATH, pathProp); } // name already defined? mitk::StringProperty::Pointer nameProp = dynamic_cast(node->GetProperty("name")); if (nameProp.IsNull() || nameProp->GetValue() == DataNode::NO_NAME_VALUE()) { // name already defined in BaseData mitk::StringProperty::Pointer baseDataNameProp = dynamic_cast(node->GetData()->GetProperty("name").GetPointer()); if (baseDataNameProp.IsNull() || baseDataNameProp->GetValue() == DataNode::NO_NAME_VALUE()) { // name neither defined in node, nor in BaseData -> name = filebasename; nameProp = mitk::StringProperty::New(this->GetRegisteredMimeType().GetFilenameWithoutExtension(filePath)); node->SetProperty("name", nameProp); } else { // name defined in BaseData! nameProp = mitk::StringProperty::New(baseDataNameProp->GetValue()); node->SetProperty("name", nameProp); } } // visibility if (!node->GetProperty("visible")) { node->SetVisibility(true); } } } diff --git a/Modules/Core/src/IO/mitkGeometryDataReaderService.cpp b/Modules/Core/src/IO/mitkGeometryDataReaderService.cpp index c95ed5eac5..0b13ffe89c 100644 --- a/Modules/Core/src/IO/mitkGeometryDataReaderService.cpp +++ b/Modules/Core/src/IO/mitkGeometryDataReaderService.cpp @@ -1,112 +1,112 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK #include "mitkGeometryDataReaderService.h" #include "mitkGeometry3DToXML.h" #include "mitkIOMimeTypes.h" #include "mitkProportionalTimeGeometryToXML.h" // STL #include #include mitk::GeometryDataReaderService::GeometryDataReaderService() : AbstractFileReader(IOMimeTypes::GEOMETRY_DATA_MIMETYPE(), "MITK Geometry Data Reader") { RegisterService(); } mitk::GeometryDataReaderService::~GeometryDataReaderService() { } -std::vector> mitk::GeometryDataReaderService::Read() +std::vector> mitk::GeometryDataReaderService::DoRead() { // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); std::vector> result; InputStream stream(this); TiXmlDocument doc; stream >> doc; if (!doc.Error()) { TiXmlHandle docHandle(&doc); for (TiXmlElement *geomDataElement = docHandle.FirstChildElement("GeometryData").ToElement(); geomDataElement != nullptr; geomDataElement = geomDataElement->NextSiblingElement()) { for (TiXmlElement *currentElement = geomDataElement->FirstChildElement(); currentElement != nullptr; currentElement = currentElement->NextSiblingElement()) { // different geometries could have been serialized from a GeometryData // object: std::string tagName = currentElement->Value(); if (tagName == "Geometry3D") { Geometry3D::Pointer restoredGeometry = Geometry3DToXML::FromXML(currentElement); if (restoredGeometry.IsNotNull()) { GeometryData::Pointer newGeometryData = GeometryData::New(); newGeometryData->SetGeometry(restoredGeometry); result.push_back(newGeometryData.GetPointer()); } else { MITK_ERROR << "Invalid tag encountered. Skipping."; } } else if (tagName == "ProportionalTimeGeometry") { ProportionalTimeGeometry::Pointer restoredTimeGeometry = ProportionalTimeGeometryToXML::FromXML(currentElement); if (restoredTimeGeometry.IsNotNull()) { GeometryData::Pointer newGeometryData = GeometryData::New(); newGeometryData->SetTimeGeometry(restoredTimeGeometry); result.push_back(newGeometryData.GetPointer()); } else { MITK_ERROR << "Invalid tag encountered. Skipping."; } } } // for child of } // for } else { mitkThrow() << "Parsing error at line " << doc.ErrorRow() << ", col " << doc.ErrorCol() << ": " << doc.ErrorDesc(); } if (result.empty()) { mitkThrow() << "Did not read a single GeometryData object from input."; } return result; } mitk::GeometryDataReaderService::GeometryDataReaderService(const mitk::GeometryDataReaderService &other) : mitk::AbstractFileReader(other) { } mitk::GeometryDataReaderService *mitk::GeometryDataReaderService::Clone() const { return new GeometryDataReaderService(*this); } diff --git a/Modules/Core/src/IO/mitkIOMetaInformationPropertyConstants.cpp b/Modules/Core/src/IO/mitkIOMetaInformationPropertyConstants.cpp new file mode 100644 index 0000000000..a57c640107 --- /dev/null +++ b/Modules/Core/src/IO/mitkIOMetaInformationPropertyConstants.cpp @@ -0,0 +1,52 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkIOMetaInformationPropertyConstants.h" + +namespace mitk +{ + + PropertyKeyPath IOMetaInformationPropertyConstants::READER_DESCRIPTION() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "description" }); + } + + PropertyKeyPath IOMetaInformationPropertyConstants::READER_VERSION() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "version" }); + } + + PropertyKeyPath IOMetaInformationPropertyConstants::READER_MIME_NAME() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "mime", "name" }); + } + + PropertyKeyPath IOMetaInformationPropertyConstants::READER_MIME_CATEGORY() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "mime", "category" }); + } + + PropertyKeyPath IOMetaInformationPropertyConstants::READER_INPUTLOCATION() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "inputlocation" }); + } + + PropertyKeyPath IOMetaInformationPropertyConstants::READER_OPTION_ROOT() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "option" }); + } + + PropertyKeyPath IOMetaInformationPropertyConstants::READER_OPTIONS_ANY() + { + return READER_OPTION_ROOT().AddAnyElement(); + } +} diff --git a/Modules/Core/src/IO/mitkImageVtkLegacyIO.cpp b/Modules/Core/src/IO/mitkImageVtkLegacyIO.cpp index cc5cb322a4..c7f39ed4d5 100644 --- a/Modules/Core/src/IO/mitkImageVtkLegacyIO.cpp +++ b/Modules/Core/src/IO/mitkImageVtkLegacyIO.cpp @@ -1,113 +1,113 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkImageVtkLegacyIO.h" #include "mitkIOMimeTypes.h" #include "mitkImage.h" #include "mitkImageVtkReadAccessor.h" #include #include #include #include #include namespace mitk { ImageVtkLegacyIO::ImageVtkLegacyIO() : AbstractFileIO(Image::GetStaticNameOfClass(), IOMimeTypes::VTK_IMAGE_LEGACY_MIMETYPE(), "VTK Legacy Image") { Options defaultOptions; defaultOptions["Save as binary file"] = false; this->SetDefaultWriterOptions(defaultOptions); this->RegisterService(); } - std::vector ImageVtkLegacyIO::Read() + std::vector ImageVtkLegacyIO::DoRead() { // The legay vtk reader cannot work with input streams const std::string fileName = this->GetLocalFileName(); vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(fileName.c_str()); reader->Update(); if (reader->GetOutput() != nullptr) { mitk::Image::Pointer output = mitk::Image::New(); output->Initialize(reader->GetOutput()); output->SetVolume(reader->GetOutput()->GetScalarPointer()); std::vector result; result.push_back(output.GetPointer()); return result; } else { mitkThrow() << "vtkStructuredPointsReader error: " << vtkErrorCode::GetStringFromErrorCode(reader->GetErrorCode()); } } IFileIO::ConfidenceLevel ImageVtkLegacyIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(this->GetLocalFileName().c_str()); if (reader->IsFileStructuredPoints()) { return Supported; } return Unsupported; } void ImageVtkLegacyIO::Write() { ValidateOutputLocation(); const auto *input = dynamic_cast(this->GetInput()); vtkSmartPointer writer = vtkSmartPointer::New(); // The legacy vtk image writer cannot write to streams LocalFile localFile(this); writer->SetFileName(localFile.GetFileName().c_str()); if (us::any_cast(GetWriterOption("Save as binary file"))) { writer->SetFileTypeToBinary(); } ImageVtkReadAccessor vtkReadAccessor(Image::ConstPointer(input), nullptr, input->GetVtkImageData()); writer->SetInputData(const_cast(vtkReadAccessor.GetVtkImageData())); if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "vtkStructuredPointesWriter error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()); } } IFileIO::ConfidenceLevel ImageVtkLegacyIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const auto *input = static_cast(this->GetInput()); if (input->GetDimension() == 3) return Supported; else if (input->GetDimension() < 3) return PartiallySupported; return Unsupported; } ImageVtkLegacyIO *ImageVtkLegacyIO::IOClone() const { return new ImageVtkLegacyIO(*this); } } diff --git a/Modules/Core/src/IO/mitkImageVtkLegacyIO.h b/Modules/Core/src/IO/mitkImageVtkLegacyIO.h index 7a0256ee21..efffa19b9d 100644 --- a/Modules/Core/src/IO/mitkImageVtkLegacyIO.h +++ b/Modules/Core/src/IO/mitkImageVtkLegacyIO.h @@ -1,42 +1,43 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKIMAGEVTKLEGACYIO_H #define MITKIMAGEVTKLEGACYIO_H #include "mitkAbstractFileIO.h" namespace mitk { class ImageVtkLegacyIO : public mitk::AbstractFileIO { public: ImageVtkLegacyIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - std::vector Read() override; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; + protected: + std::vector> DoRead() override; private: ImageVtkLegacyIO *IOClone() const override; }; } #endif // MITKIMAGEVTKLEGACYIO_H diff --git a/Modules/Core/src/IO/mitkImageVtkXmlIO.cpp b/Modules/Core/src/IO/mitkImageVtkXmlIO.cpp index 8318508fc1..a7f04fc164 100644 --- a/Modules/Core/src/IO/mitkImageVtkXmlIO.cpp +++ b/Modules/Core/src/IO/mitkImageVtkXmlIO.cpp @@ -1,144 +1,144 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkImageVtkXmlIO.h" #include "mitkIOMimeTypes.h" #include "mitkImage.h" #include "mitkImageVtkReadAccessor.h" #include #include #include #include #include namespace mitk { class VtkXMLImageDataReader : public ::vtkXMLImageDataReader { public: static VtkXMLImageDataReader *New() { return new VtkXMLImageDataReader(); } vtkTypeMacro(VtkXMLImageDataReader, vtkXMLImageDataReader) void SetStream(std::istream *is) { this->Stream = is; } std::istream *GetStream() const { return this->Stream; } }; class VtkXMLImageDataWriter : public ::vtkXMLImageDataWriter { public: static VtkXMLImageDataWriter *New() { return new VtkXMLImageDataWriter(); } vtkTypeMacro(VtkXMLImageDataWriter, vtkXMLImageDataWriter) void SetStream(std::ostream *os) { this->Stream = os; } std::ostream *GetStream() const { return this->Stream; } }; ImageVtkXmlIO::ImageVtkXmlIO() : AbstractFileIO(Image::GetStaticNameOfClass(), IOMimeTypes::VTK_IMAGE_MIMETYPE(), "VTK XML Image") { this->RegisterService(); } - std::vector ImageVtkXmlIO::Read() + std::vector ImageVtkXmlIO::DoRead() { vtkSmartPointer reader = vtkSmartPointer::New(); if (this->GetInputStream()) { reader->SetStream(this->GetInputStream()); } else { reader->SetFileName(this->GetInputLocation().c_str()); } reader->Update(); if (reader->GetOutput() != nullptr) { mitk::Image::Pointer output = mitk::Image::New(); output->Initialize(reader->GetOutput()); output->SetVolume(reader->GetOutput()->GetScalarPointer()); std::vector result; result.push_back(output.GetPointer()); return result; } else { mitkThrow() << "vtkXMLImageDataReader error: " << vtkErrorCode::GetStringFromErrorCode(reader->GetErrorCode()); } } IFileIO::ConfidenceLevel ImageVtkXmlIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; if (this->GetInputStream() == nullptr) { // check if the xml vtk reader can handle the file vtkSmartPointer xmlReader = vtkSmartPointer::New(); if (xmlReader->CanReadFile(this->GetInputLocation().c_str()) != 0) { return Supported; } return Unsupported; } // in case of an input stream, VTK does not seem to have methods for // validating it return Supported; } void ImageVtkXmlIO::Write() { ValidateOutputLocation(); const auto *input = dynamic_cast(this->GetInput()); vtkSmartPointer writer = vtkSmartPointer::New(); if (this->GetOutputStream()) { writer->SetStream(this->GetOutputStream()); } else { writer->SetFileName(this->GetOutputLocation().c_str()); } ImageVtkReadAccessor vtkReadAccessor(Image::ConstPointer(input), nullptr, input->GetVtkImageData()); writer->SetInputData(const_cast(vtkReadAccessor.GetVtkImageData())); if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "vtkXMLImageDataWriter error: " << vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()); } } IFileIO::ConfidenceLevel ImageVtkXmlIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const auto *input = static_cast(this->GetInput()); if (input->GetDimension() == 3) return Supported; else if (input->GetDimension() < 3) return PartiallySupported; return Unsupported; } ImageVtkXmlIO *ImageVtkXmlIO::IOClone() const { return new ImageVtkXmlIO(*this); } } diff --git a/Modules/Core/src/IO/mitkImageVtkXmlIO.h b/Modules/Core/src/IO/mitkImageVtkXmlIO.h index bdfd759adc..5edc030350 100644 --- a/Modules/Core/src/IO/mitkImageVtkXmlIO.h +++ b/Modules/Core/src/IO/mitkImageVtkXmlIO.h @@ -1,42 +1,43 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKIMAGEVTKXMLIO_H #define MITKIMAGEVTKXMLIO_H #include "mitkAbstractFileIO.h" namespace mitk { class ImageVtkXmlIO : public mitk::AbstractFileIO { public: ImageVtkXmlIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - std::vector Read() override; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; + protected: + std::vector> DoRead() override; private: ImageVtkXmlIO *IOClone() const override; }; } #endif // MITKIMAGEVTKXMLIO_H diff --git a/Modules/Core/src/IO/mitkItkImageIO.cpp b/Modules/Core/src/IO/mitkItkImageIO.cpp index f482ea5f13..d4666d22ed 100644 --- a/Modules/Core/src/IO/mitkItkImageIO.cpp +++ b/Modules/Core/src/IO/mitkItkImageIO.cpp @@ -1,708 +1,708 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 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"; 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::Read() + 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); } } } 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); } 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/IO/mitkLegacyFileReaderService.cpp b/Modules/Core/src/IO/mitkLegacyFileReaderService.cpp index f206c43d22..d9abfa1bae 100644 --- a/Modules/Core/src/IO/mitkLegacyFileReaderService.cpp +++ b/Modules/Core/src/IO/mitkLegacyFileReaderService.cpp @@ -1,113 +1,113 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkLegacyFileReaderService.h" #include #include #include #include mitk::LegacyFileReaderService::LegacyFileReaderService(const mitk::LegacyFileReaderService &other) : mitk::AbstractFileReader(other) { } mitk::LegacyFileReaderService::LegacyFileReaderService(const std::vector &extensions, const std::string &category) : AbstractFileReader() { this->SetMimeTypePrefix(IOMimeTypes::DEFAULT_BASE_NAME() + ".legacy."); CustomMimeType customMimeType; customMimeType.SetCategory(category); for (auto extension : extensions) { if (!extension.empty() && extension[0] == '.') { extension.assign(extension.begin() + 1, extension.end()); } customMimeType.AddExtension(extension); } this->SetDescription(category); this->SetMimeType(customMimeType); m_ServiceReg = this->RegisterService(); } mitk::LegacyFileReaderService::~LegacyFileReaderService() { } ////////////////////// Reading ///////////////////////// -std::vector> mitk::LegacyFileReaderService::Read() +std::vector> mitk::LegacyFileReaderService::DoRead() { std::vector result; std::list possibleIOAdapter; std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("mitkIOAdapter"); for (auto i = allobjects.begin(); i != allobjects.end(); ++i) { auto *io = dynamic_cast(i->GetPointer()); if (io) { possibleIOAdapter.push_back(io); } else { MITK_ERROR << "Error BaseDataIO factory did not return an IOAdapterBase: " << (*i)->GetNameOfClass() << std::endl; } } const std::string path = this->GetLocalFileName(); for (auto k = possibleIOAdapter.begin(); k != possibleIOAdapter.end(); ++k) { bool canReadFile = (*k)->CanReadFile(path, "", ""); // they could read the file if (canReadFile) { BaseDataSource::Pointer ioObject = (*k)->CreateIOProcessObject(path, "", ""); ioObject->Update(); auto numberOfContents = static_cast(ioObject->GetNumberOfOutputs()); if (numberOfContents > 0) { BaseData::Pointer baseData; for (int i = 0; i < numberOfContents; ++i) { baseData = dynamic_cast(ioObject->GetOutputs()[i].GetPointer()); if (baseData) // this is what's wanted, right? { result.push_back(baseData); } } } break; } } if (result.empty()) { mitkThrow() << "Could not read file '" << path << "'"; } return result; } mitk::LegacyFileReaderService *mitk::LegacyFileReaderService::Clone() const { return new LegacyFileReaderService(*this); } diff --git a/Modules/Core/src/IO/mitkLegacyFileReaderService.h b/Modules/Core/src/IO/mitkLegacyFileReaderService.h index 598220201a..096ba02263 100644 --- a/Modules/Core/src/IO/mitkLegacyFileReaderService.h +++ b/Modules/Core/src/IO/mitkLegacyFileReaderService.h @@ -1,42 +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 MITKLEGACYFILEREADERSERVICE_H #define MITKLEGACYFILEREADERSERVICE_H #include namespace mitk { // This class wraps mitk::FileReader instances registered as // "mitkIOAdapter" via the ITK object factory system as a // micro service. class LegacyFileReaderService : public mitk::AbstractFileReader { public: LegacyFileReaderService(const LegacyFileReaderService &other); LegacyFileReaderService(const std::vector &extensions, const std::string &category); ~LegacyFileReaderService() override; using AbstractFileReader::Read; - std::vector> Read() override; + + protected: + std::vector> DoRead() override; private: LegacyFileReaderService *Clone() const override; us::ServiceRegistration m_ServiceReg; }; } // namespace mitk #endif /* MITKLEGACYFILEREADERSERVICE_H */ diff --git a/Modules/Core/src/IO/mitkPointSetReaderService.cpp b/Modules/Core/src/IO/mitkPointSetReaderService.cpp index 24dac6ba8c..68cc84f562 100644 --- a/Modules/Core/src/IO/mitkPointSetReaderService.cpp +++ b/Modules/Core/src/IO/mitkPointSetReaderService.cpp @@ -1,275 +1,275 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK #include "mitkPointSetReaderService.h" #include "mitkGeometry3DToXML.h" #include "mitkIOMimeTypes.h" #include "mitkProportionalTimeGeometry.h" // STL #include #include #include #include mitk::PointSetReaderService::PointSetReaderService() : AbstractFileReader(CustomMimeType(IOMimeTypes::POINTSET_MIMETYPE()), "MITK Point Set Reader") { RegisterService(); } mitk::PointSetReaderService::~PointSetReaderService() { } -std::vector> mitk::PointSetReaderService::Read() +std::vector> mitk::PointSetReaderService::DoRead() { // Switch the current locale to "C" LocaleSwitch localeSwitch("C"); std::vector> result; InputStream stream(this); TiXmlDocument doc; stream >> doc; if (!doc.Error()) { TiXmlHandle docHandle(&doc); // unsigned int pointSetCounter(0); for (TiXmlElement *currentPointSetElement = docHandle.FirstChildElement("point_set_file").FirstChildElement("point_set").ToElement(); currentPointSetElement != nullptr; currentPointSetElement = currentPointSetElement->NextSiblingElement()) { mitk::PointSet::Pointer newPointSet = mitk::PointSet::New(); // time geometry assembled for addition after all points // else the SetPoint method would already transform the points that we provide it mitk::ProportionalTimeGeometry::Pointer timeGeometry = mitk::ProportionalTimeGeometry::New(); if (currentPointSetElement->FirstChildElement("time_series") != nullptr) { for (TiXmlElement *currentTimeSeries = currentPointSetElement->FirstChildElement("time_series")->ToElement(); currentTimeSeries != nullptr; currentTimeSeries = currentTimeSeries->NextSiblingElement()) { unsigned int currentTimeStep(0); TiXmlElement *currentTimeSeriesID = currentTimeSeries->FirstChildElement("time_series_id"); currentTimeStep = atoi(currentTimeSeriesID->GetText()); timeGeometry->Expand(currentTimeStep + 1); // expand (default to identity) in any case TiXmlElement *geometryElem = currentTimeSeries->FirstChildElement("Geometry3D"); if (geometryElem) { Geometry3D::Pointer geometry = Geometry3DToXML::FromXML(geometryElem); if (geometry.IsNotNull()) { timeGeometry->SetTimeStepGeometry(geometry, currentTimeStep); } else { MITK_ERROR << "Could not deserialize Geometry3D element."; } } else { MITK_WARN << "Fallback to legacy behavior: defining PointSet geometry as identity"; } newPointSet = this->ReadPoints(newPointSet, currentTimeSeries, currentTimeStep); } } else { newPointSet = this->ReadPoints(newPointSet, currentPointSetElement, 0); } newPointSet->SetTimeGeometry(timeGeometry); result.push_back(newPointSet.GetPointer()); } } else { mitkThrow() << "Parsing error at line " << doc.ErrorRow() << ", col " << doc.ErrorCol() << ": " << doc.ErrorDesc(); } return result; } mitk::BaseGeometry::Pointer mitk::PointSetReaderService::ReadGeometry(TiXmlElement *parentElement) { TiXmlElement *geometryElem = parentElement->FirstChildElement("geometry3d"); if (!geometryElem) return nullptr; // data to generate AffineTransform3D::MatrixType matrix; AffineTransform3D::OffsetType offset; bool isImageGeometry(false); unsigned int frameOfReferenceID(0); BaseGeometry::BoundsArrayType bounds; bool somethingMissing(false); // find data in xml structure TiXmlElement *imageGeometryElem = geometryElem->FirstChildElement("image_geometry"); if (imageGeometryElem) { std::string igs = imageGeometryElem->GetText(); isImageGeometry = igs == "true" || igs == "TRUE" || igs == "1"; } else somethingMissing = true; TiXmlElement *frameOfReferenceElem = geometryElem->FirstChildElement("frame_of_reference_id"); if (frameOfReferenceElem) { frameOfReferenceID = atoi(frameOfReferenceElem->GetText()); } else somethingMissing = true; TiXmlElement *indexToWorldElem = geometryElem->FirstChildElement("index_to_world"); if (indexToWorldElem) { TiXmlElement *matrixElem = indexToWorldElem->FirstChildElement("matrix3x3"); TiXmlElement *offsetElem = indexToWorldElem->FirstChildElement("offset"); if (indexToWorldElem && offsetElem) { TiXmlElement *col0 = matrixElem->FirstChildElement("column_0"); TiXmlElement *col1 = matrixElem->FirstChildElement("column_1"); TiXmlElement *col2 = matrixElem->FirstChildElement("column_2"); if (col0 && col1 && col2) { somethingMissing |= TIXML_SUCCESS != col0->QueryDoubleAttribute("x", &matrix[0][0]); somethingMissing |= TIXML_SUCCESS != col0->QueryDoubleAttribute("y", &matrix[1][0]); somethingMissing |= TIXML_SUCCESS != col0->QueryDoubleAttribute("z", &matrix[2][0]); somethingMissing |= TIXML_SUCCESS != col1->QueryDoubleAttribute("x", &matrix[0][1]); somethingMissing |= TIXML_SUCCESS != col1->QueryDoubleAttribute("y", &matrix[1][1]); somethingMissing |= TIXML_SUCCESS != col1->QueryDoubleAttribute("z", &matrix[2][1]); somethingMissing |= TIXML_SUCCESS != col2->QueryDoubleAttribute("x", &matrix[0][2]); somethingMissing |= TIXML_SUCCESS != col2->QueryDoubleAttribute("y", &matrix[1][2]); somethingMissing |= TIXML_SUCCESS != col2->QueryDoubleAttribute("z", &matrix[2][2]); } else somethingMissing = true; somethingMissing |= TIXML_SUCCESS != offsetElem->QueryDoubleAttribute("x", &offset[0]); somethingMissing |= TIXML_SUCCESS != offsetElem->QueryDoubleAttribute("y", &offset[1]); somethingMissing |= TIXML_SUCCESS != offsetElem->QueryDoubleAttribute("z", &offset[2]); } else somethingMissing = true; TiXmlElement *boundsElem = geometryElem->FirstChildElement("bounds"); if (boundsElem) { TiXmlElement *minBoundsElem = boundsElem->FirstChildElement("min"); TiXmlElement *maxBoundsElem = boundsElem->FirstChildElement("max"); if (minBoundsElem && maxBoundsElem) { somethingMissing |= TIXML_SUCCESS != minBoundsElem->QueryDoubleAttribute("x", &bounds[0]); somethingMissing |= TIXML_SUCCESS != minBoundsElem->QueryDoubleAttribute("y", &bounds[2]); somethingMissing |= TIXML_SUCCESS != minBoundsElem->QueryDoubleAttribute("z", &bounds[4]); somethingMissing |= TIXML_SUCCESS != maxBoundsElem->QueryDoubleAttribute("x", &bounds[1]); somethingMissing |= TIXML_SUCCESS != maxBoundsElem->QueryDoubleAttribute("y", &bounds[3]); somethingMissing |= TIXML_SUCCESS != maxBoundsElem->QueryDoubleAttribute("z", &bounds[5]); } else somethingMissing = true; } else somethingMissing = true; } else somethingMissing = true; if (somethingMissing) { MITK_ERROR << "XML structure of geometry inside a PointSet file broken. Refusing to build Geometry3D"; return nullptr; } else { Geometry3D::Pointer g = Geometry3D::New(); g->SetImageGeometry(isImageGeometry); g->SetFrameOfReferenceID(frameOfReferenceID); g->SetBounds(bounds); AffineTransform3D::Pointer transform = AffineTransform3D::New(); transform->SetMatrix(matrix); transform->SetOffset(offset); g->SetIndexToWorldTransform(transform); return g.GetPointer(); } } mitk::PointSet::Pointer mitk::PointSetReaderService::ReadPoints(mitk::PointSet::Pointer newPointSet, TiXmlElement *currentTimeSeries, unsigned int currentTimeStep) { if (currentTimeSeries->FirstChildElement("point") != nullptr) { for (TiXmlElement *currentPoint = currentTimeSeries->FirstChildElement("point")->ToElement(); currentPoint != nullptr; currentPoint = currentPoint->NextSiblingElement()) { unsigned int id(0); auto spec((mitk::PointSpecificationType)0); double x(0.0); double y(0.0); double z(0.0); id = atoi(currentPoint->FirstChildElement("id")->GetText()); if (currentPoint->FirstChildElement("specification") != nullptr) { spec = (mitk::PointSpecificationType)atoi(currentPoint->FirstChildElement("specification")->GetText()); } x = atof(currentPoint->FirstChildElement("x")->GetText()); y = atof(currentPoint->FirstChildElement("y")->GetText()); z = atof(currentPoint->FirstChildElement("z")->GetText()); mitk::Point3D point; mitk::FillVector3D(point, x, y, z); newPointSet->SetPoint(id, point, spec, currentTimeStep); } } else { if (currentTimeStep != newPointSet->GetTimeSteps() + 1) { newPointSet->Expand(currentTimeStep + 1); // expand time step series with empty time step } } return newPointSet; } mitk::PointSetReaderService::PointSetReaderService(const mitk::PointSetReaderService &other) : mitk::AbstractFileReader(other) { } mitk::PointSetReaderService *mitk::PointSetReaderService::Clone() const { return new mitk::PointSetReaderService(*this); } diff --git a/Modules/Core/src/IO/mitkPointSetReaderService.h b/Modules/Core/src/IO/mitkPointSetReaderService.h index 42854d4aec..5aba51b592 100644 --- a/Modules/Core/src/IO/mitkPointSetReaderService.h +++ b/Modules/Core/src/IO/mitkPointSetReaderService.h @@ -1,60 +1,62 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_POINT_SET_READER_SERVICE__H_ #define _MITK_POINT_SET_READER_SERVICE__H_ // MITK #include #include class TiXmlElement; namespace mitk { /** * @internal * * @brief reads xml representations of mitk::PointSets from a file * * Reader for xml files containing one or multiple xml represenations of * mitk::PointSet. If multiple mitk::PointSet objects are stored in one file, * these are assigned to multiple BaseData objects. * * The reader is able to read the old 3D Pointsets without the "specification" and "timeseries" tags and the new 4D * Pointsets. * * @ingroup IO */ class PointSetReaderService : public AbstractFileReader { public: PointSetReaderService(); ~PointSetReaderService() override; using AbstractFileReader::Read; - std::vector> Read() override; + + protected: + std::vector> DoRead() override; private: PointSetReaderService(const PointSetReaderService &other); mitk::BaseGeometry::Pointer ReadGeometry(TiXmlElement *parentElement); mitk::PointSet::Pointer ReadPoints(mitk::PointSet::Pointer newPointSet, TiXmlElement *currentTimeSeries, unsigned int currentTimeStep); PointSetReaderService *Clone() const override; }; } #endif diff --git a/Modules/Core/src/IO/mitkRawImageFileReader.cpp b/Modules/Core/src/IO/mitkRawImageFileReader.cpp index 7bb6559e8c..ee98b414be 100644 --- a/Modules/Core/src/IO/mitkRawImageFileReader.cpp +++ b/Modules/Core/src/IO/mitkRawImageFileReader.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 "mitkRawImageFileReader.h" #include "mitkIOConstants.h" #include "mitkIOMimeTypes.h" #include "mitkITKImageImport.h" #include "mitkImageCast.h" #include #include #include mitk::RawImageFileReaderService::RawImageFileReaderService() : AbstractFileReader(CustomMimeType(IOMimeTypes::RAW_MIMETYPE()), "ITK raw image reader") { Options defaultOptions; defaultOptions[IOConstants::PIXEL_TYPE()] = IOConstants::PIXEL_TYPE_USHORT(); std::vector pixelEnum; pixelEnum.push_back(IOConstants::PIXEL_TYPE_UCHAR()); pixelEnum.push_back(IOConstants::PIXEL_TYPE_CHAR()); pixelEnum.push_back(IOConstants::PIXEL_TYPE_USHORT()); pixelEnum.push_back(IOConstants::PIXEL_TYPE_SHORT()); pixelEnum.push_back(IOConstants::PIXEL_TYPE_UINT()); pixelEnum.push_back(IOConstants::PIXEL_TYPE_INT()); pixelEnum.push_back(IOConstants::PIXEL_TYPE_FLOAT()); pixelEnum.push_back(IOConstants::PIXEL_TYPE_DOUBLE()); defaultOptions[IOConstants::PIXEL_TYPE_ENUM()] = pixelEnum; defaultOptions[IOConstants::DIMENSION()] = std::string("3"); std::vector dimEnum; dimEnum.push_back("2"); dimEnum.push_back("3"); defaultOptions[IOConstants::DIMENSION_ENUM()] = dimEnum; defaultOptions[IOConstants::ENDIANNESS()] = IOConstants::ENDIANNESS_LITTLE(); std::vector endianEnum; endianEnum.push_back(IOConstants::ENDIANNESS_LITTLE()); endianEnum.push_back(IOConstants::ENDIANNESS_BIG()); defaultOptions[IOConstants::ENDIANNESS_ENUM()] = endianEnum; defaultOptions[IOConstants::SIZE_X()] = 0; defaultOptions[IOConstants::SIZE_Y()] = 0; defaultOptions[IOConstants::SIZE_Z()] = 0; // defaultOptions[IOConstants::SIZE_T()] = 0; this->SetDefaultOptions(defaultOptions); this->RegisterService(); } mitk::RawImageFileReaderService::RawImageFileReaderService(const mitk::RawImageFileReaderService &other) : AbstractFileReader(other) { } -std::vector> mitk::RawImageFileReaderService::Read() +std::vector> mitk::RawImageFileReaderService::DoRead() { std::vector result; const std::string path = this->GetLocalFileName(); const Options options = this->GetOptions(); const std::string dimensionality = options.find(IOConstants::DIMENSION())->second.ToString(); const std::string pixelType = options.find(IOConstants::PIXEL_TYPE())->second.ToString(); EndianityType endianity = options.find(IOConstants::ENDIANNESS())->second.ToString() == IOConstants::ENDIANNESS_LITTLE() ? LITTLE : BIG; int dimensions[4]; dimensions[0] = us::any_cast(options.find(IOConstants::SIZE_X())->second); dimensions[1] = us::any_cast(options.find(IOConstants::SIZE_Y())->second); dimensions[2] = us::any_cast(options.find(IOConstants::SIZE_Z())->second); dimensions[3] = 0; // us::any_cast(options.find(IOConstants::SIZE_T())->second); // check file dimensionality and pixel type and perform reading according to it if (dimensionality == "2") { if (pixelType == IOConstants::PIXEL_TYPE_CHAR()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_UCHAR()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_SHORT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_USHORT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_UINT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_INT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_FLOAT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_DOUBLE()) result.push_back(TypedRead(path, endianity, dimensions)); else { MITK_INFO << "Error while reading raw file: Dimensionality or pixel type not supported or not properly set" << std::endl; } } else if (dimensionality == "3") { if (pixelType == IOConstants::PIXEL_TYPE_CHAR()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_UCHAR()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_SHORT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_USHORT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_UINT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_INT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_FLOAT()) result.push_back(TypedRead(path, endianity, dimensions)); else if (pixelType == IOConstants::PIXEL_TYPE_DOUBLE()) result.push_back(TypedRead(path, endianity, dimensions)); else { MITK_INFO << "Error while reading raw file: Dimensionality or pixel type not supported or not properly set" << std::endl; } } else { MITK_INFO << "Error while reading raw file: Dimensionality not supported" << std::endl; } return result; } template mitk::BaseData::Pointer mitk::RawImageFileReaderService::TypedRead(const std::string &path, EndianityType endianity, int *size) { typedef itk::Image ImageType; typedef itk::ImageFileReader ReaderType; typedef itk::RawImageIO IOType; typename ReaderType::Pointer reader = ReaderType::New(); typename IOType::Pointer io = IOType::New(); io->SetFileDimensionality(VImageDimensions); for (unsigned short int dim = 0; dim < VImageDimensions; ++dim) { io->SetDimensions(dim, size[dim]); } if (endianity == LITTLE) { io->SetByteOrderToLittleEndian(); } else if (endianity == BIG) { io->SetByteOrderToBigEndian(); } else { MITK_INFO << "Warning: endianity not properly set. Resulting image might be incorrect"; } reader->SetImageIO(io); reader->SetFileName(path); try { reader->Update(); } catch ( const itk::ExceptionObject &err ) { MITK_ERROR << "An error occurred during the raw image reading process: "; MITK_INFO << err.GetDescription() << std::endl; } mitk::Image::Pointer image = mitk::Image::New(); mitk::CastToMitkImage(reader->GetOutput(), image); image->SetVolume(reader->GetOutput()->GetBufferPointer()); return image.GetPointer(); } mitk::RawImageFileReaderService *mitk::RawImageFileReaderService::Clone() const { return new RawImageFileReaderService(*this); } diff --git a/Modules/Core/src/IO/mitkRawImageFileReader.h b/Modules/Core/src/IO/mitkRawImageFileReader.h index 4752290370..3ad97d9199 100644 --- a/Modules/Core/src/IO/mitkRawImageFileReader.h +++ b/Modules/Core/src/IO/mitkRawImageFileReader.h @@ -1,54 +1,54 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKRAWIMAGEFILEREADER_H_ #define MITKRAWIMAGEFILEREADER_H_ #include "mitkAbstractFileReader.h" namespace mitk { /** * The user must set the dimensionality, the dimensions and the pixel type. * If they are incorrect, the image will not be opened or the visualization will be incorrect. */ class RawImageFileReaderService : public AbstractFileReader { public: /** Supported pixel types. */ typedef enum { UCHAR, SCHAR, USHORT, SSHORT, UINT, SINT, FLOAT, DOUBLE } IOPixelType; /** Endianity of bits. */ typedef enum { LITTLE, BIG } EndianityType; RawImageFileReaderService(); protected: RawImageFileReaderService(const RawImageFileReaderService &other); - std::vector> Read() override; + std::vector> DoRead() override; using mitk::AbstractFileReader::Read; private: template mitk::BaseData::Pointer TypedRead(const std::string &path, EndianityType endianity, int *size); RawImageFileReaderService *Clone() const override; /** Vector containing dimensions of image to be read. */ itk::Vector m_Dimensions; }; } // namespace mitk #endif /* MITKRAWIMAGEFILEREADER_H_ */ diff --git a/Modules/Core/src/IO/mitkSurfaceStlIO.cpp b/Modules/Core/src/IO/mitkSurfaceStlIO.cpp index fa919c857a..e64e9a1634 100644 --- a/Modules/Core/src/IO/mitkSurfaceStlIO.cpp +++ b/Modules/Core/src/IO/mitkSurfaceStlIO.cpp @@ -1,160 +1,160 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSurfaceStlIO.h" #include "mitkIOMimeTypes.h" #include "mitkLocaleSwitch.h" #include "mitkSurface.h" #include #include #include #include #include #include #include namespace mitk { std::string SurfaceStlIO::OPTION_MERGE_POINTS() { static std::string s = "Merge points"; return s; } std::string SurfaceStlIO::OPTION_TAG_SOLIDS() { static std::string s = "Tag solids"; return s; } std::string SurfaceStlIO::OPTION_CLEAN() { static std::string s = "Clean poly data"; return s; } SurfaceStlIO::SurfaceStlIO() : SurfaceVtkIO(Surface::GetStaticNameOfClass(), IOMimeTypes::STEREOLITHOGRAPHY_MIMETYPE(), "Stereolithography") { Options defaultOptions; defaultOptions[OPTION_MERGE_POINTS()] = us::Any(true); defaultOptions[OPTION_TAG_SOLIDS()] = us::Any(false); defaultOptions[OPTION_CLEAN()] = us::Any(true); this->SetDefaultReaderOptions(defaultOptions); this->RegisterService(); } - std::vector> SurfaceStlIO::Read() + std::vector> SurfaceStlIO::DoRead() { LocaleSwitch localeSwitch("C"); Options options = this->GetReaderOptions(); mitk::Surface::Pointer output = mitk::Surface::New(); vtkSmartPointer stlReader = vtkSmartPointer::New(); stlReader->SetFileName(this->GetLocalFileName().c_str()); bool mergePoints = true; bool tagSolids = false; bool cleanData = true; try { mergePoints = us::any_cast(options[OPTION_MERGE_POINTS()]); tagSolids = us::any_cast(options[OPTION_TAG_SOLIDS()]); cleanData = us::any_cast(options[OPTION_CLEAN()]); } catch (const us::BadAnyCastException &e) { MITK_WARN << "Unexpected error: " << e.what(); } stlReader->SetMerging(mergePoints); stlReader->SetScalarTags(tagSolids); vtkSmartPointer normalsGenerator = vtkSmartPointer::New(); normalsGenerator->SetInputConnection(stlReader->GetOutputPort()); vtkSmartPointer algo = normalsGenerator; if (cleanData) { vtkSmartPointer cleanPolyDataFilter = vtkSmartPointer::New(); cleanPolyDataFilter->SetInputConnection(normalsGenerator->GetOutputPort()); cleanPolyDataFilter->PieceInvariantOff(); cleanPolyDataFilter->ConvertLinesToPointsOff(); cleanPolyDataFilter->ConvertPolysToLinesOff(); cleanPolyDataFilter->ConvertStripsToPolysOff(); if (mergePoints) { cleanPolyDataFilter->PointMergingOn(); } algo = cleanPolyDataFilter; } algo->Update(); if (algo->GetOutput() != nullptr) { vtkSmartPointer surfaceWithNormals = algo->GetOutput(); output->SetVtkPolyData(surfaceWithNormals); } std::vector result; result.push_back(output.GetPointer()); return result; } void SurfaceStlIO::Write() { LocaleSwitch localeSwitch("C"); ValidateOutputLocation(); const auto *input = dynamic_cast(this->GetInput()); const unsigned int timesteps = input->GetTimeGeometry()->CountTimeSteps(); for (unsigned int t = 0; t < timesteps; ++t) { std::string fileName; vtkSmartPointer polyData = this->GetPolyData(t, fileName); vtkSmartPointer triangleFilter = vtkSmartPointer::New(); triangleFilter->SetInputData(polyData); vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputConnection(triangleFilter->GetOutputPort()); // The vtk stl writer cannot write to streams LocalFile localFile(this); writer->SetFileName(localFile.GetFileName().c_str()); if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "Error during surface writing" << (writer->GetErrorCode() ? std::string(": ") + vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) : std::string()); } if (this->GetOutputStream() && input->GetTimeGeometry()->CountTimeSteps() > 1) { MITK_WARN << "Writing multiple time-steps to output streams is not supported. " << "Only the first time-step will be written"; break; } } } SurfaceStlIO *SurfaceStlIO::IOClone() const { return new SurfaceStlIO(*this); } } diff --git a/Modules/Core/src/IO/mitkSurfaceStlIO.h b/Modules/Core/src/IO/mitkSurfaceStlIO.h index 4d9ef51748..ffe36e1e60 100644 --- a/Modules/Core/src/IO/mitkSurfaceStlIO.h +++ b/Modules/Core/src/IO/mitkSurfaceStlIO.h @@ -1,43 +1,45 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_SURFACE_STL_IO_H_ #define _MITK_SURFACE_STL_IO_H_ #include "mitkSurfaceVtkIO.h" namespace mitk { class SurfaceStlIO : public mitk::SurfaceVtkIO { public: SurfaceStlIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - std::vector> Read() override; // -------------- AbstractFileWriter ------------- void Write() override; + protected: + std::vector> DoRead() override; + private: SurfaceStlIO *IOClone() const override; static std::string OPTION_MERGE_POINTS(); static std::string OPTION_TAG_SOLIDS(); static std::string OPTION_CLEAN(); }; } #endif //_MITK_SURFACE_STL_IO_H_ diff --git a/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.cpp b/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.cpp index 36b25c483a..1dbc32bc49 100644 --- a/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.cpp +++ b/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.cpp @@ -1,117 +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 "mitkSurfaceVtkLegacyIO.h" #include "mitkIOMimeTypes.h" #include "mitkSurface.h" #include #include #include #include #include #include namespace mitk { SurfaceVtkLegacyIO::SurfaceVtkLegacyIO() : SurfaceVtkIO(Surface::GetStaticNameOfClass(), IOMimeTypes::VTK_POLYDATA_LEGACY_MIMETYPE(), "VTK Legacy PolyData") { Options defaultOptions; defaultOptions["Save as binary file"] = false; this->SetDefaultWriterOptions(defaultOptions); this->RegisterService(); } - std::vector> SurfaceVtkLegacyIO::Read() + std::vector> SurfaceVtkLegacyIO::DoRead() { mitk::Surface::Pointer output = mitk::Surface::New(); // The legay vtk reader cannot work with input streams const std::string fileName = this->GetLocalFileName(); vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(fileName.c_str()); reader->Update(); if (reader->GetOutput() != nullptr) { output->SetVtkPolyData(reader->GetOutput()); } else { mitkThrow() << "vtkPolyDataReader error: " << vtkErrorCode::GetStringFromErrorCode(reader->GetErrorCode()); } std::vector result; result.push_back(output.GetPointer()); return result; } IFileIO::ConfidenceLevel SurfaceVtkLegacyIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(this->GetLocalFileName().c_str()); if (reader->IsFilePolyData()) { if (std::strcmp(reader->GetHeader(), "vtk output") == 0) { return Supported; } else return PartiallySupported; } return Unsupported; } void SurfaceVtkLegacyIO::Write() { ValidateOutputLocation(); const auto *input = dynamic_cast(this->GetInput()); const unsigned int timesteps = input->GetTimeGeometry()->CountTimeSteps(); for (unsigned int t = 0; t < timesteps; ++t) { std::string fileName; vtkSmartPointer polyData = this->GetPolyData(t, fileName); vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputData(polyData); if (us::any_cast(GetWriterOption("Save as binary file"))) { writer->SetFileTypeToBinary(); } // The legacy vtk poly data writer cannot write to streams LocalFile localFile(this); writer->SetFileName(localFile.GetFileName().c_str()); if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "Error during surface writing" << (writer->GetErrorCode() ? std::string(": ") + vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) : std::string()); } if (this->GetOutputStream() && input->GetTimeGeometry()->CountTimeSteps() > 1) { MITK_WARN << "Writing multiple time-steps to output streams is not supported. " << "Only the first time-step will be written"; break; } } } SurfaceVtkLegacyIO *SurfaceVtkLegacyIO::IOClone() const { return new SurfaceVtkLegacyIO(*this); } } diff --git a/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.h b/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.h index f9e0c062e1..7be88eaceb 100644 --- a/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.h +++ b/Modules/Core/src/IO/mitkSurfaceVtkLegacyIO.h @@ -1,43 +1,45 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_SURFACE_VTK_LEGACY_IO_H_ #define _MITK_SURFACE_VTK_LEGACY_IO_H_ #include "mitkSurfaceVtkIO.h" #include "mitkBaseData.h" namespace mitk { class SurfaceVtkLegacyIO : public mitk::SurfaceVtkIO { public: SurfaceVtkLegacyIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - std::vector Read() override; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; + protected: + std::vector> DoRead() override; + private: SurfaceVtkLegacyIO *IOClone() const override; }; } #endif //_MITK_SURFACE_VTK_LEGACY_IO_H_ diff --git a/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.cpp b/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.cpp index 394ab06b25..5b05614279 100644 --- a/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.cpp +++ b/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.cpp @@ -1,152 +1,152 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkSurfaceVtkXmlIO.h" #include "mitkIOMimeTypes.h" #include "mitkSurface.h" #include #include #include #include namespace mitk { class VtkXMLPolyDataReader : public ::vtkXMLPolyDataReader { public: static VtkXMLPolyDataReader *New() { return new VtkXMLPolyDataReader(); } vtkTypeMacro(VtkXMLPolyDataReader, vtkXMLPolyDataReader) void SetStream(std::istream *is) { this->Stream = is; } std::istream *GetStream() const { return this->Stream; } }; class VtkXMLPolyDataWriter : public ::vtkXMLPolyDataWriter { public: static VtkXMLPolyDataWriter *New() { return new VtkXMLPolyDataWriter(); } vtkTypeMacro(VtkXMLPolyDataWriter, vtkXMLPolyDataWriter) void SetStream(std::ostream *os) { this->Stream = os; } std::ostream *GetStream() const { return this->Stream; } }; SurfaceVtkXmlIO::SurfaceVtkXmlIO() : SurfaceVtkIO(Surface::GetStaticNameOfClass(), IOMimeTypes::VTK_POLYDATA_MIMETYPE(), "VTK XML PolyData") { this->RegisterService(); } - std::vector> SurfaceVtkXmlIO::Read() + std::vector> SurfaceVtkXmlIO::DoRead() { mitk::Surface::Pointer output = mitk::Surface::New(); vtkSmartPointer reader = vtkSmartPointer::New(); if (this->GetInputStream()) { reader->SetStream(this->GetInputStream()); } else { reader->SetFileName(this->GetInputLocation().c_str()); } reader->Update(); if (reader->GetOutput() != nullptr) { output->SetVtkPolyData(reader->GetOutput()); } else { mitkThrow() << "vtkXMLPolyDataReader error: " << vtkErrorCode::GetStringFromErrorCode(reader->GetErrorCode()); } std::vector result; result.push_back(output.GetPointer()); return result; } IFileIO::ConfidenceLevel SurfaceVtkXmlIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; if (this->GetInputStream() == nullptr) { // check if the xml vtk reader can handle the file vtkSmartPointer xmlReader = vtkSmartPointer::New(); if (xmlReader->CanReadFile(this->GetInputLocation().c_str()) != 0) { return Supported; } return Unsupported; } // in case of an input stream, VTK does not seem to have methods for // validating it return Supported; } void SurfaceVtkXmlIO::Write() { ValidateOutputLocation(); const auto *input = dynamic_cast(this->GetInput()); const unsigned int timesteps = input->GetTimeGeometry()->CountTimeSteps(); for (unsigned int t = 0; t < timesteps; ++t) { std::string fileName; vtkSmartPointer polyData = this->GetPolyData(t, fileName); if (polyData.Get() == nullptr) { mitkThrow() << "Cannot write empty surface"; } vtkSmartPointer writer = vtkSmartPointer::New(); writer->SetInputData(polyData); if (this->GetOutputStream()) { if (input->GetTimeGeometry()->CountTimeSteps() > 1) { MITK_WARN << "Writing multiple time-steps to output streams is not supported. " << "Only the first time-step will be written"; } writer->SetStream(this->GetOutputStream()); } else { writer->SetFileName(fileName.c_str()); } if (writer->Write() == 0 || writer->GetErrorCode() != 0) { mitkThrow() << "Error during surface writing" << (writer->GetErrorCode() ? std::string(": ") + vtkErrorCode::GetStringFromErrorCode(writer->GetErrorCode()) : std::string()); } if (this->GetOutputStream()) break; } } SurfaceVtkXmlIO *SurfaceVtkXmlIO::IOClone() const { return new SurfaceVtkXmlIO(*this); } } diff --git a/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.h b/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.h index 5fb62f41d6..ed4daa3a49 100644 --- a/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.h +++ b/Modules/Core/src/IO/mitkSurfaceVtkXmlIO.h @@ -1,43 +1,45 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_SURFACE_VTK_XML_IO_H_ #define _MITK_SURFACE_VTK_XML_IO_H_ #include "mitkSurfaceVtkIO.h" #include "mitkBaseData.h" namespace mitk { class SurfaceVtkXmlIO : public mitk::SurfaceVtkIO { public: SurfaceVtkXmlIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - std::vector Read() override; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; + protected: + std::vector> DoRead() override; + private: SurfaceVtkXmlIO *IOClone() const override; }; } #endif //_MITK_SURFACE_VTK_XML_IO_H_ diff --git a/Modules/Core/src/mitkCoreActivator.cpp b/Modules/Core/src/mitkCoreActivator.cpp index 17a64bf8ae..4e2a3363d9 100644 --- a/Modules/Core/src/mitkCoreActivator.cpp +++ b/Modules/Core/src/mitkCoreActivator.cpp @@ -1,321 +1,357 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkCoreActivator.h" +#include +#include + // File IO +#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "mitkLegacyFileWriterService.h" #include #include #include // Micro Services #include #include #include #include #include #include #include #include #include #include // ITK "injects" static initialization code for IO factories // via the itkImageIOFactoryRegisterManager.h header (which // is generated in the application library build directory). // To ensure that the code is called *before* the CppMicroServices // static initialization code (which triggers the Activator::Start // method), we include the ITK header here. #include void HandleMicroServicesMessages(us::MsgType type, const char *msg) { switch (type) { case us::DebugMsg: MITK_DEBUG << msg; break; case us::InfoMsg: MITK_INFO << msg; break; case us::WarningMsg: MITK_WARN << msg; break; case us::ErrorMsg: MITK_ERROR << msg; break; } } void AddMitkAutoLoadPaths(const std::string &programPath) { us::ModuleSettings::AddAutoLoadPath(programPath); #ifdef __APPLE__ // Walk up three directories since that is where the .dylib files are located // for build trees. std::string additionalPath = programPath; bool addPath = true; for (int i = 0; i < 3; ++i) { std::size_t index = additionalPath.find_last_of('/'); if (index != std::string::npos) { additionalPath = additionalPath.substr(0, index); } else { addPath = false; break; } } if (addPath) { us::ModuleSettings::AddAutoLoadPath(additionalPath); } #endif } +void AddPropertyPersistence(const mitk::PropertyKeyPath& propPath) +{ + mitk::CoreServicePointer persistenceService(mitk::CoreServices::GetPropertyPersistence()); + + auto info = mitk::PropertyPersistenceInfo::New(); + if (propPath.IsExplicit()) + { + std::string name = mitk::PropertyKeyPathToPropertyName(propPath); + std::string key = name; + std::replace(key.begin(), key.end(), '.', '_'); + info->SetNameAndKey(name, key); + } + else + { + std::string key = mitk::PropertyKeyPathToPersistenceKeyRegEx(propPath); + std::string keyTemplate = mitk::PropertyKeyPathToPersistenceKeyTemplate(propPath); + std::string propRegEx = mitk::PropertyKeyPathToPropertyRegEx(propPath); + std::string propTemplate = mitk::PropertyKeyPathToPersistenceNameTemplate(propPath); + info->UseRegEx(propRegEx, propTemplate, key, keyTemplate); + } + + persistenceService->AddInfo(info); +} + class FixedNiftiImageIO : public itk::NiftiImageIO { public: /** Standard class typedefs. */ typedef FixedNiftiImageIO Self; typedef itk::NiftiImageIO Superclass; typedef itk::SmartPointer Pointer; /** Method for creation through the object factory. */ itkNewMacro(Self) /** Run-time type information (and related methods). */ itkTypeMacro(FixedNiftiImageIO, Superclass) bool SupportsDimension(unsigned long dim) override { return dim > 1 && dim < 5; } }; void MitkCoreActivator::Load(us::ModuleContext *context) { // Handle messages from CppMicroServices us::installMsgHandler(HandleMicroServicesMessages); this->m_Context = context; // Add the current application directory to the auto-load paths. // This is useful for third-party executables. std::string programPath = mitk::IOUtil::GetProgramPath(); if (programPath.empty()) { MITK_WARN << "Could not get the program path."; } else { AddMitkAutoLoadPaths(programPath); } // m_RenderingManager = mitk::RenderingManager::New(); // context->RegisterService(renderingManager.GetPointer()); m_PlanePositionManager.reset(new mitk::PlanePositionManagerService); context->RegisterService(m_PlanePositionManager.get()); m_PropertyAliases.reset(new mitk::PropertyAliases); context->RegisterService(m_PropertyAliases.get()); m_PropertyDescriptions.reset(new mitk::PropertyDescriptions); context->RegisterService(m_PropertyDescriptions.get()); m_PropertyExtensions.reset(new mitk::PropertyExtensions); context->RegisterService(m_PropertyExtensions.get()); m_PropertyFilters.reset(new mitk::PropertyFilters); context->RegisterService(m_PropertyFilters.get()); m_PropertyPersistence.reset(new mitk::PropertyPersistence); context->RegisterService(m_PropertyPersistence.get()); m_PropertyRelations.reset(new mitk::PropertyRelations); context->RegisterService(m_PropertyRelations.get()); m_MimeTypeProvider.reset(new mitk::MimeTypeProvider); m_MimeTypeProvider->Start(); m_MimeTypeProviderReg = context->RegisterService(m_MimeTypeProvider.get()); this->RegisterDefaultMimeTypes(); this->RegisterItkReaderWriter(); this->RegisterVtkReaderWriter(); // Add custom Reader / Writer Services m_FileReaders.push_back(new mitk::PointSetReaderService()); m_FileWriters.push_back(new mitk::PointSetWriterService()); m_FileReaders.push_back(new mitk::GeometryDataReaderService()); m_FileWriters.push_back(new mitk::GeometryDataWriterService()); m_FileReaders.push_back(new mitk::RawImageFileReaderService()); + //add properties that should be persistent (if possible/supported by the writer) + AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_DESCRIPTION()); + AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_INPUTLOCATION()); + AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_MIME_CATEGORY()); + AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_MIME_NAME()); + AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_VERSION()); + AddPropertyPersistence(mitk::IOMetaInformationPropertyConstants::READER_OPTIONS_ANY()); + /* There IS an option to exchange ALL vtkTexture instances against vtkNeverTranslucentTextureFactory. This code is left here as a reminder, just in case we might need to do that some time. vtkNeverTranslucentTextureFactory* textureFactory = vtkNeverTranslucentTextureFactory::New(); vtkObjectFactory::RegisterFactory( textureFactory ); textureFactory->Delete(); */ this->RegisterLegacyWriter(); } void MitkCoreActivator::Unload(us::ModuleContext *) { for (auto &elem : m_FileReaders) { delete elem; } for (auto &elem : m_FileWriters) { delete elem; } for (auto &elem : m_FileIOs) { delete elem; } for (auto &elem : m_LegacyWriters) { delete elem; } // The mitk::ModuleContext* argument of the Unload() method // will always be 0 for the Mitk library. It makes no sense // to use it at this stage anyway, since all libraries which // know about the module system have already been unloaded. // we need to close the internal service tracker of the // MimeTypeProvider class here. Otherwise it // would hold on to the ModuleContext longer than it is // actually valid. m_MimeTypeProviderReg.Unregister(); m_MimeTypeProvider->Stop(); for (std::vector::const_iterator mimeTypeIter = m_DefaultMimeTypes.begin(), iterEnd = m_DefaultMimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { delete *mimeTypeIter; } } void MitkCoreActivator::RegisterDefaultMimeTypes() { // Register some default mime-types std::vector mimeTypes = mitk::IOMimeTypes::Get(); for (std::vector::const_iterator mimeTypeIter = mimeTypes.begin(), iterEnd = mimeTypes.end(); mimeTypeIter != iterEnd; ++mimeTypeIter) { m_DefaultMimeTypes.push_back(*mimeTypeIter); m_Context->RegisterService(m_DefaultMimeTypes.back()); } } void MitkCoreActivator::RegisterItkReaderWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("itkImageIOBase"); for (auto &allobject : allobjects) { auto *io = dynamic_cast(allobject.GetPointer()); // NiftiImageIO does not provide a correct "SupportsDimension()" methods // and the supported read/write extensions are not ordered correctly if (dynamic_cast(io)) continue; // Use a custom mime-type for GDCMImageIO below if (dynamic_cast(allobject.GetPointer())) { // MITK provides its own DICOM reader (which internally uses GDCMImageIO). continue; } if (io) { m_FileIOs.push_back(new mitk::ItkImageIO(io)); } else { MITK_WARN << "Error ImageIO factory did not return an ImageIOBase: " << (allobject)->GetNameOfClass(); } } FixedNiftiImageIO::Pointer itkNiftiIO = FixedNiftiImageIO::New(); mitk::ItkImageIO *niftiIO = new mitk::ItkImageIO(mitk::IOMimeTypes::NIFTI_MIMETYPE(), itkNiftiIO.GetPointer(), 0); m_FileIOs.push_back(niftiIO); } void MitkCoreActivator::RegisterVtkReaderWriter() { m_FileIOs.push_back(new mitk::SurfaceVtkXmlIO()); m_FileIOs.push_back(new mitk::SurfaceStlIO()); m_FileIOs.push_back(new mitk::SurfaceVtkLegacyIO()); m_FileIOs.push_back(new mitk::ImageVtkXmlIO()); m_FileIOs.push_back(new mitk::ImageVtkLegacyIO()); } void MitkCoreActivator::RegisterLegacyWriter() { std::list allobjects = itk::ObjectFactoryBase::CreateAllInstance("IOWriter"); for (auto i = allobjects.begin(); i != allobjects.end(); ++i) { mitk::FileWriter::Pointer io = dynamic_cast(i->GetPointer()); if (io) { std::string description = std::string("Legacy ") + io->GetNameOfClass() + " Writer"; mitk::IFileWriter *writer = new mitk::LegacyFileWriterService(io, description); m_LegacyWriters.push_back(writer); } else { MITK_ERROR << "Error IOWriter override is not of type mitk::FileWriter: " << (*i)->GetNameOfClass() << std::endl; } } } US_EXPORT_MODULE_ACTIVATOR(MitkCoreActivator) // Call CppMicroservices initialization code at the end of the file. // This especially ensures that VTK object factories have already // been registered (VTK initialization code is injected by implicitly // include VTK header files at the top of this file). US_INITIALIZE_MODULE diff --git a/Modules/Core/test/files.cmake b/Modules/Core/test/files.cmake index f98fb8a5df..5efc282bfa 100644 --- a/Modules/Core/test/files.cmake +++ b/Modules/Core/test/files.cmake @@ -1,193 +1,194 @@ # 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 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/mitkFileReaderRegistryTest.cpp b/Modules/Core/test/mitkFileReaderRegistryTest.cpp index 66670f36c3..94b414c0fc 100644 --- a/Modules/Core/test/mitkFileReaderRegistryTest.cpp +++ b/Modules/Core/test/mitkFileReaderRegistryTest.cpp @@ -1,212 +1,212 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkAbstractFileReader.h" #include "mitkFileReaderRegistry.h" #include "mitkIFileReader.h" #include "mitkTestingMacros.h" #include #include #include class DummyReader : public mitk::AbstractFileReader { public: DummyReader(const DummyReader &other) : mitk::AbstractFileReader(other) {} DummyReader(const std::string &mimeTypeName, const std::string &extension, int priority) : mitk::AbstractFileReader() { mitk::CustomMimeType mimeType(mimeTypeName); mimeType.AddExtension(extension); mimeType.SetComment("This is a dummy description"); this->SetMimeType(mimeType); this->SetRanking(priority); m_ServiceReg = this->RegisterService(); } ~DummyReader() override { if (m_ServiceReg) m_ServiceReg.Unregister(); } using mitk::AbstractFileReader::Read; - std::vector> Read() override + std::vector> DoRead() override { std::vector result; return result; } private: DummyReader *Clone() const override { return new DummyReader(*this); } us::ServiceRegistration m_ServiceReg; }; // End of internal dummy reader class DummyReader2 : public mitk::AbstractFileReader { public: DummyReader2(const DummyReader2 &other) : mitk::AbstractFileReader(other) {} DummyReader2(const std::string &mimeTypeName, const std::string &extension, int priority) : mitk::AbstractFileReader() { mitk::CustomMimeType mimeType(mimeTypeName); mimeType.AddExtension(extension); mimeType.SetComment("This is a second dummy description"); this->SetMimeType(mimeType); this->SetRanking(priority); m_ServiceReg = this->RegisterService(); } ~DummyReader2() override { if (m_ServiceReg) m_ServiceReg.Unregister(); } using mitk::AbstractFileReader::Read; - std::vector> Read() override + std::vector> DoRead() override { std::vector result; return result; } private: DummyReader2 *Clone() const override { return new DummyReader2(*this); } us::ServiceRegistration m_ServiceReg; }; // End of internal dummy reader 2 /** * TODO */ int mitkFileReaderRegistryTest(int /*argc*/, char * /*argv*/ []) { // always start with this! MITK_TEST_BEGIN("FileReaderRegistry"); // mitk::FileReaderRegistry::Pointer frm = mitk::FileReaderRegistry::New(); // MITK_TEST_CONDITION_REQUIRED(argc == 2,"Testing FileReaderRegistry instantiation"); // DummyReader testDR("application/dummy", "test",1); // DummyReader otherDR("application/dummy2", "other",1); // MITK_TEST_CONDITION_REQUIRED(!testDR.CanRead("/this/is/a/folder/file.tes"),"Negative test of default CanRead() // implementation"); // mitk::FileReaderRegistry* readerRegistry = new mitk::FileReaderRegistry; // mitk::IFileReader* returned = readerRegistry->GetReader("bla.test"); // MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(testDR) != returned,"Testing correct // retrieval of FileReader 1/2"); // returned = readerRegistry->GetReader("other"); // MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(otherDR) != returned,"Testing correct // retrieval of FileReader 2/2"); // DummyReader mediocreTestDR("application/dummy", "test", 20); // DummyReader prettyFlyTestDR("application/dummy", "test", 50); // DummyReader2 awesomeTestDR("application/dummy", "test", 100); // returned = readerRegistry->GetReader("test"); // MITK_TEST_CONDITION_REQUIRED(dynamic_cast(returned), "Testing correct priorized retrieval of // FileReader: Best reader"); // Now to give those readers some options, then we will try again // mitk::IFileReader::OptionList options; // options.push_back(std::make_pair("isANiceGuy", true)); // mediocreTestDR.SetOptions(options); // options.clear(); // options.push_back(std::make_pair("canFly", true)); // prettyFlyTestDR.SetOptions(options); // options.push_back(std::make_pair("isAwesome", true)); // awesomeTestDR.SetOptions(options); //note: awesomeReader canFly and isAwesome // // Reset Options, use to define what we want the reader to do // options.clear(); // mitk::IFileReader::OptionNames optionsFilter; // optionsFilter.push_back("canFly"); // returned = readerRegistry->GetReader("test", optionsFilter); // MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(awesomeTestDR) != returned, "Testing // correct retrieval of FileReader with Options: Best reader with options"); // optionsFilter.push_back("isAwesome"); // returned = readerRegistry->GetReader("test", optionsFilter); // MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(awesomeTestDR) != returned, "Testing // correct retrieval of FileReader with multiple Options: Best reader with options"); // optionsFilter.clear(); // optionsFilter.push_back("isANiceGuy"); // returned = readerRegistry->GetReader("test", optionsFilter); // MITK_TEST_CONDITION_REQUIRED(returned && &static_cast(mediocreTestDR) != returned, "Testing // correct retrieval of specific FileReader with Options: Low priority reader with specific option"); // optionsFilter.push_back("canFly"); // returned = readerRegistry->GetReader("test", optionsFilter); // MITK_TEST_CONDITION_REQUIRED(returned == nullptr, "Testing correct return of 0 value when no matching reader was // found"); // // Onward to test the retrieval of multiple readers // std::vector< mitk::IFileReader* > returnedList; // returnedList = readerRegistry->GetReaders("test", optionsFilter); // MITK_TEST_CONDITION_REQUIRED(returnedList.empty(), "Testing correct return of zero readers when no matching reader // was found, asking for all compatibles"); // optionsFilter.clear(); // optionsFilter.push_back("canFly"); // returnedList = readerRegistry->GetReaders("test", optionsFilter); // MITK_TEST_CONDITION_REQUIRED(returnedList.size() == 2, "Testing correct return of two readers when two matching // reader was found, asking for all compatibles"); // MITK_TEST_CONDITION_REQUIRED(dynamic_cast(returnedList.front()), "Testing correct priorization of // returned Readers with options 1/2"); // optionsFilter.clear(); // optionsFilter.push_back("isAwesome"); // returnedList = readerRegistry->GetReaders("test", optionsFilter); // MITK_TEST_CONDITION_REQUIRED(returnedList.size() == 1, "Testing correct return of one readers when one matching // reader was found, asking for all compatibles"); // MITK_TEST_CONDITION_REQUIRED(dynamic_cast(returnedList.front()), "Testing correctness of result // from former query"); // And now to verify a working read chain for a mps file: // mitk::PointSetReader::Pointer psr = mitk::PointSetReader::New(); // std::vector basedata; // basedata = mitk::FileReaderRegistry::Read("F://Build//MITK-Data//pointSet.mps"); // MITK_TEST_CONDITION_REQUIRED(basedata.size() > 0, "Testing correct read of PointSet"); // Testing templated call to ReaderRegistry // mitk::PointSet::Pointer pointset = mitk::FileReaderRegistry::Read< mitk::PointSet // >("F://Build//MITK-Data//pointSet.mps"); // MITK_TEST_CONDITION_REQUIRED(pointset.IsNotNull(), "Testing templated call of Read()"); // And now for something completely different... (Debug) // mitk::LegacyFileReaderService::Pointer lfr = mitk::LegacyFileReaderService::New(".nrrd", "Nearly Raw Raster Data"); // returned = mitk::FileReaderRegistry::GetReader(".nrrd"); // MITK_TEST_CONDITION_REQUIRED(lfr == returned, "Testing correct retrieval of specific FileReader with Options: Low // priority reader with specific option"); // std::vector image = // mitk::FileReaderRegistry::Read("F://Build//MITK-Data//Pic2DplusT.nrrd"); // MITK_TEST_CONDITION_REQUIRED(image.size() > 0, "Testing whether image was returned or not"); // mitk::Image::Pointer image2 = dynamic_cast (image.front().GetPointer()); // MITK_TEST_CONDITION_REQUIRED(image2.IsNotNull(), "Testing if BaseData is an image"); // Delete this here because it will call the PrototypeServiceFactory::Unget() method // of the dummy readers. // delete readerRegistry; // always end with this! MITK_TEST_END(); } diff --git a/Modules/Core/test/mitkIOUtilTest.cpp b/Modules/Core/test/mitkIOUtilTest.cpp index e1e1f73e98..1a7ddd83f3 100644 --- a/Modules/Core/test/mitkIOUtilTest.cpp +++ b/Modules/Core/test/mitkIOUtilTest.cpp @@ -1,231 +1,293 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 class mitkIOUtilTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkIOUtilTestSuite); MITK_TEST(TestTempMethods); MITK_TEST(TestSaveEmptyData); MITK_TEST(TestLoadAndSaveImage); MITK_TEST(TestNullLoad); MITK_TEST(TestNullSave); MITK_TEST(TestLoadAndSavePointSet); MITK_TEST(TestLoadAndSaveSurface); MITK_TEST(TestTempMethodsForUniqueFilenames); MITK_TEST(TestTempMethodsForUniqueFilenames); + MITK_TEST(TestIOMetaInformation); CPPUNIT_TEST_SUITE_END(); private: std::string m_ImagePath; std::string m_SurfacePath; std::string m_PointSetPath; public: void setUp() override { m_ImagePath = GetTestDataFilePath("Pic3D.nrrd"); m_SurfacePath = GetTestDataFilePath("binary.stl"); m_PointSetPath = GetTestDataFilePath("pointSet.mps"); } void TestSaveEmptyData() { mitk::Surface::Pointer data = mitk::Surface::New(); CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(data, "/tmp/dummy"), mitk::Exception); } void TestTempMethods() { std::string tmpPath = mitk::IOUtil::GetTempPath(); CPPUNIT_ASSERT(!tmpPath.empty()); std::ofstream tmpFile; std::string tmpFilePath = mitk::IOUtil::CreateTemporaryFile(tmpFile); CPPUNIT_ASSERT(tmpFile && tmpFile.is_open()); CPPUNIT_ASSERT(tmpFilePath.size() > tmpPath.size()); CPPUNIT_ASSERT(tmpFilePath.substr(0, tmpPath.size()) == tmpPath); tmpFile.close(); CPPUNIT_ASSERT(std::remove(tmpFilePath.c_str()) == 0); std::string programPath = mitk::IOUtil::GetProgramPath(); CPPUNIT_ASSERT(!programPath.empty()); std::ofstream tmpFile2; std::string tmpFilePath2 = mitk::IOUtil::CreateTemporaryFile(tmpFile2, "my-XXXXXX", programPath); CPPUNIT_ASSERT(tmpFile2 && tmpFile2.is_open()); CPPUNIT_ASSERT(tmpFilePath2.size() > programPath.size()); CPPUNIT_ASSERT(tmpFilePath2.substr(0, programPath.size()) == programPath); tmpFile2.close(); CPPUNIT_ASSERT(std::remove(tmpFilePath2.c_str()) == 0); std::ofstream tmpFile3; std::string tmpFilePath3 = mitk::IOUtil::CreateTemporaryFile(tmpFile3, std::ios_base::binary, "my-XXXXXX.TXT", programPath); CPPUNIT_ASSERT(tmpFile3 && tmpFile3.is_open()); CPPUNIT_ASSERT(tmpFilePath3.size() > programPath.size()); CPPUNIT_ASSERT(tmpFilePath3.substr(0, programPath.size()) == programPath); CPPUNIT_ASSERT(tmpFilePath3.substr(tmpFilePath3.size() - 13, 3) == "my-"); CPPUNIT_ASSERT(tmpFilePath3.substr(tmpFilePath3.size() - 4) == ".TXT"); tmpFile3.close(); // CPPUNIT_ASSERT(std::remove(tmpFilePath3.c_str()) == 0) std::string tmpFilePath4 = mitk::IOUtil::CreateTemporaryFile(); std::ofstream file; file.open(tmpFilePath4.c_str()); CPPUNIT_ASSERT_MESSAGE("Testing if file exists after CreateTemporaryFile()", file.is_open()); CPPUNIT_ASSERT_THROW(mitk::IOUtil::CreateTemporaryFile(tmpFile2, "XX"), mitk::Exception); std::string tmpDir = mitk::IOUtil::CreateTemporaryDirectory(); CPPUNIT_ASSERT(tmpDir.size() > tmpPath.size()); CPPUNIT_ASSERT(tmpDir.substr(0, tmpPath.size()) == tmpPath); CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(tmpDir.c_str())); std::string tmpDir2 = mitk::IOUtil::CreateTemporaryDirectory("my-XXXXXX", programPath); CPPUNIT_ASSERT(tmpDir2.size() > programPath.size()); CPPUNIT_ASSERT(tmpDir2.substr(0, programPath.size()) == programPath); CPPUNIT_ASSERT(itksys::SystemTools::RemoveADirectory(tmpDir2.c_str())); } void TestTempMethodsForUniqueFilenames() { int numberOfFiles = 100; // create 100 empty files std::vector v100filenames; for (int i = 0; i < numberOfFiles; i++) { v100filenames.push_back(mitk::IOUtil::CreateTemporaryFile()); } // check if all of them are unique for (int i = 0; i < numberOfFiles; i++) for (int j = 0; j < numberOfFiles; j++) { if (i != j) { std::stringstream message; message << "Checking if file " << i << " and file " << j << " are different, which should be the case because each of them should be unique."; CPPUNIT_ASSERT_MESSAGE(message.str(), (v100filenames.at(i) != v100filenames.at(j))); } } // delete all the files / clean up for (int i = 0; i < numberOfFiles; i++) { std::remove(v100filenames.at(i).c_str()); } } void TestLoadAndSaveImage() { mitk::Image::Pointer img1 = mitk::IOUtil::Load(m_ImagePath); CPPUNIT_ASSERT(img1.IsNotNull()); std::ofstream tmpStream; std::string imagePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "diffpic3d-XXXXXX.nrrd"); tmpStream.close(); std::string imagePath2 = mitk::IOUtil::CreateTemporaryFile(tmpStream, "diffpic3d-XXXXXX.nii.gz"); tmpStream.close(); // the cases where no exception should be thrown CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(img1, imagePath)); CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(img1.GetPointer(), imagePath2)); // load data which does not exist CPPUNIT_ASSERT_THROW(mitk::IOUtil::Load("fileWhichDoesNotExist.nrrd"), mitk::Exception); // delete the files after the test is done std::remove(imagePath.c_str()); std::remove(imagePath2.c_str()); mitk::Image::Pointer relativImage = mitk::ImageGenerator::GenerateGradientImage(4, 4, 4, 1); std::string imagePath3 = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.nrrd"); tmpStream.close(); mitk::IOUtil::Save(relativImage, imagePath3); CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Load(imagePath3)); std::remove(imagePath3.c_str()); } /** * \brief This method calls all available load methods with a nullpointer and an empty pathand expects an exception **/ void TestNullLoad() { CPPUNIT_ASSERT_THROW(mitk::IOUtil::Load(""), mitk::Exception); } /** * \brief This method calls the save method (to which all other convenience save methods reference) with null *parameters **/ void TestNullSave() { CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(nullptr, mitk::IOUtil::CreateTemporaryFile()), mitk::Exception); CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(mitk::Image::New().GetPointer(), ""), mitk::Exception); } void TestLoadAndSavePointSet() { mitk::PointSet::Pointer pointset = mitk::IOUtil::Load(m_PointSetPath); CPPUNIT_ASSERT(pointset.IsNotNull()); std::ofstream tmpStream; std::string pointSetPath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mps"); tmpStream.close(); std::string pointSetPathWithDefaultExtension = mitk::IOUtil::CreateTemporaryFile(tmpStream, "XXXXXX.mps"); tmpStream.close(); std::string pointSetPathWithoutDefaultExtension = mitk::IOUtil::CreateTemporaryFile(tmpStream); tmpStream.close(); // the cases where no exception should be thrown CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(pointset, pointSetPathWithDefaultExtension)); // test if defaultextension is inserted if no extension is present CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(pointset, pointSetPathWithoutDefaultExtension.c_str())); // delete the files after the test is done std::remove(pointSetPath.c_str()); std::remove(pointSetPathWithDefaultExtension.c_str()); std::remove(pointSetPathWithoutDefaultExtension.c_str()); } void TestLoadAndSaveSurface() { mitk::Surface::Pointer surface = mitk::IOUtil::Load(m_SurfacePath); CPPUNIT_ASSERT(surface.IsNotNull()); std::ofstream tmpStream; std::string surfacePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "diffsurface-XXXXXX.stl"); // the cases where no exception should be thrown CPPUNIT_ASSERT_NO_THROW(mitk::IOUtil::Save(surface, surfacePath)); // test if exception is thrown as expected on unknown extsension CPPUNIT_ASSERT_THROW(mitk::IOUtil::Save(surface, "testSurface.xXx"), mitk::Exception); // delete the files after the test is done std::remove(surfacePath.c_str()); } + + std::string GenerateMetaDictKey(const mitk::PropertyKeyPath& propKey) + { + auto result = mitk::PropertyKeyPathToPropertyName(propKey); + std::replace(result.begin(), result.end(), '.', '_'); + return result; + } + + std::string GetValueFromMetaDict(const itk::MetaDataDictionary& dict, const mitk::PropertyKeyPath& propKey) + { + auto metaValueBase = dict.Get(GenerateMetaDictKey(propKey)); + auto metaValue = dynamic_cast*>(metaValueBase); + return metaValue->GetMetaDataObjectValue(); + } + + void TestIOMetaInformation() + { + mitk::Image::Pointer img = mitk::IOUtil::Load(m_ImagePath); + CPPUNIT_ASSERT(img.IsNotNull()); + + auto value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_DESCRIPTION()).c_str())->GetValueAsString(); + CPPUNIT_ASSERT_EQUAL(std::string("ITK NrrdImageIO"), value); + value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_INPUTLOCATION()).c_str())->GetValueAsString(); + CPPUNIT_ASSERT_EQUAL(m_ImagePath, value); + value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_MIME_CATEGORY()).c_str())->GetValueAsString(); + CPPUNIT_ASSERT_EQUAL(std::string("Images"), value); + value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_MIME_NAME()).c_str())->GetValueAsString(); + CPPUNIT_ASSERT_EQUAL(std::string("application/vnd.mitk.image.nrrd"), value); + value = img->GetProperty(mitk::PropertyKeyPathToPropertyName(mitk::IOMetaInformationPropertyConstants::READER_VERSION()).c_str())->GetValueAsString(); + CPPUNIT_ASSERT_EQUAL(std::string(MITK_VERSION_STRING), value); + + //check if the information is persistet correctly on save. + std::ofstream tmpStream; + std::string imagePath = mitk::IOUtil::CreateTemporaryFile(tmpStream, "ioMeta_XXXXXX.nrrd"); + tmpStream.close(); + mitk::IOUtil::Save(img, imagePath); + + auto io = itk::NrrdImageIO::New(); + io->SetFileName(imagePath); + io->ReadImageInformation(); + auto metaDict = io->GetMetaDataDictionary(); + + auto metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_DESCRIPTION()); + CPPUNIT_ASSERT_EQUAL(std::string("ITK NrrdImageIO"), metaValue); + metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_INPUTLOCATION()); + CPPUNIT_ASSERT_EQUAL(m_ImagePath, metaValue); + metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_MIME_CATEGORY()); + CPPUNIT_ASSERT_EQUAL(std::string("Images"), metaValue); + metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_MIME_NAME()); + CPPUNIT_ASSERT_EQUAL(std::string("application/vnd.mitk.image.nrrd"), metaValue); + metaValue = GetValueFromMetaDict(metaDict, mitk::IOMetaInformationPropertyConstants::READER_VERSION()); + CPPUNIT_ASSERT_EQUAL(std::string(MITK_VERSION_STRING), metaValue); + + // delete the files after the test is done + std::remove(imagePath.c_str()); + } + }; MITK_TEST_SUITE_REGISTRATION(mitkIOUtil) diff --git a/Modules/Core/test/mitkPreferenceListReaderOptionsFunctorTest.cpp b/Modules/Core/test/mitkPreferenceListReaderOptionsFunctorTest.cpp index c779a6aefc..c4af10cd1c 100644 --- a/Modules/Core/test/mitkPreferenceListReaderOptionsFunctorTest.cpp +++ b/Modules/Core/test/mitkPreferenceListReaderOptionsFunctorTest.cpp @@ -1,199 +1,199 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPreferenceListReaderOptionsFunctor.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include #include #include #include #include namespace mitk { class TestFileReaderService : public mitk::AbstractFileReader { public: TestFileReaderService(const std::string &description) : AbstractFileReader(CustomMimeType("TestMimeType"), description) { m_ServiceRegistration = RegisterService(); }; ~TestFileReaderService() override { }; using AbstractFileReader::Read; - std::vector> Read() override + std::vector> DoRead() override { std::vector> result; return result; }; ConfidenceLevel GetConfidenceLevel() const override { return Supported; }; private: TestFileReaderService * Clone() const override { return new TestFileReaderService(*this); }; us::ServiceRegistration m_ServiceRegistration; }; } // namespace mitk class mitkPreferenceListReaderOptionsFunctorTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPreferenceListReaderOptionsFunctorTestSuite); MITK_TEST(UsePreferenceList); MITK_TEST(UseBlackList); MITK_TEST(UseNoList); MITK_TEST(UseBlackAndPreferenceList); MITK_TEST(UseOverlappingBlackAndPreferenceList); MITK_TEST(UsePreferenceListWithInexistantReaders); MITK_TEST(UseAllBlackedList); CPPUNIT_TEST_SUITE_END(); private: std::string m_ImagePath; mitk::PreferenceListReaderOptionsFunctor::ListType preference; mitk::PreferenceListReaderOptionsFunctor::ListType black; mitk::PreferenceListReaderOptionsFunctor::ListType emptyList; mitk::TestFileReaderService* m_NormalService; mitk::TestFileReaderService* m_PrefService; mitk::TestFileReaderService* m_BlackService; mitk::CustomMimeType* m_TestMimeType; public: void setUp() override { m_ImagePath = GetTestDataFilePath("BallBinary30x30x30.nrrd"); preference = { "Prefered Test Service" }; black = { "Unwanted Test Service" }; emptyList = {}; m_TestMimeType = new mitk::CustomMimeType("TestMimeType"); m_TestMimeType->AddExtension("nrrd"); m_TestMimeType->SetCategory(mitk::IOMimeTypes::CATEGORY_IMAGES()); m_TestMimeType->SetComment("Test mime type"); us::ModuleContext *context = us::GetModuleContext(); us::ServiceProperties props; props[us::ServiceConstants::SERVICE_RANKING()] = 10; context->RegisterService(m_TestMimeType, props); m_NormalService = new mitk::TestFileReaderService("Normal Test Service"); m_PrefService = new mitk::TestFileReaderService("Prefered Test Service"); m_BlackService = new mitk::TestFileReaderService("Unwanted Test Service"); } void tearDown() override { delete m_PrefService; delete m_BlackService; delete m_NormalService; delete m_TestMimeType; } void UsePreferenceList() { mitk::IOUtil::LoadInfo info(m_ImagePath); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, emptyList); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); CPPUNIT_ASSERT_EQUAL(std::string("Prefered Test Service"), description); } void UseNoList() { mitk::IOUtil::LoadInfo info(m_ImagePath); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(emptyList, emptyList); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); CPPUNIT_ASSERT_EQUAL(std::string("Normal Test Service"), description); } void UseBlackList() { mitk::IOUtil::LoadInfo info(m_ImagePath); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(emptyList, black); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); CPPUNIT_ASSERT(description != "Unwanted Test Service"); } void UseBlackAndPreferenceList() { mitk::IOUtil::LoadInfo info(m_ImagePath); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, black); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); CPPUNIT_ASSERT_EQUAL(std::string("Prefered Test Service"), description); } void UseOverlappingBlackAndPreferenceList() { mitk::IOUtil::LoadInfo info(m_ImagePath); black.push_back("Prefered Test Service"); black.push_back("Normal Test Service"); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, black); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); CPPUNIT_ASSERT_EQUAL(std::string("ITK NrrdImageIO"), description); } void UsePreferenceListWithInexistantReaders() { mitk::IOUtil::LoadInfo info(m_ImagePath); preference.push_back("InexistantReader"); mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(preference, emptyList); CPPUNIT_ASSERT(true == functor(info)); auto description = info.m_ReaderSelector.GetSelected().GetDescription(); CPPUNIT_ASSERT_EQUAL(std::string("Prefered Test Service"), description); } void UseAllBlackedList() { mitk::IOUtil::LoadInfo info(m_ImagePath); for (auto reader : info.m_ReaderSelector.Get()) { black.push_back(reader.GetDescription()); } mitk::PreferenceListReaderOptionsFunctor functor = mitk::PreferenceListReaderOptionsFunctor(emptyList, black); CPPUNIT_ASSERT_THROW(functor(info), mitk::Exception); } }; MITK_TEST_SUITE_REGISTRATION(mitkPreferenceListReaderOptionsFunctor) diff --git a/Modules/Core/test/mitkPropertyKeyPathTest.cpp b/Modules/Core/test/mitkPropertyKeyPathTest.cpp index 338df2a20f..2ef686768e 100644 --- a/Modules/Core/test/mitkPropertyKeyPathTest.cpp +++ b/Modules/Core/test/mitkPropertyKeyPathTest.cpp @@ -1,314 +1,321 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPropertyKeyPath.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkPropertyKeyPathTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPropertyKeyPathTestSuite); MITK_TEST(AccessFunctions); MITK_TEST(PropertyKeyPathToPropertyRegEx); MITK_TEST(PropertyKeyPathToPersistenceKeyRegEx); MITK_TEST(PropertyKeyPathToPersistenceKeyTemplate); MITK_TEST(PropertyKeyPathToPersistenceNameTemplate); MITK_TEST(PropertyNameToPropertyKeyPath); MITK_TEST(PropertyKeyPathToPropertyName); MITK_TEST(ExecutePropertyRegEx); MITK_TEST(Comparison); + MITK_TEST(InitializerList); CPPUNIT_TEST_SUITE_END(); private: mitk::PropertyKeyPath simplePath; mitk::PropertyKeyPath simplePath2; mitk::PropertyKeyPath deepPath; mitk::PropertyKeyPath deepPath_withAnyElement; mitk::PropertyKeyPath deepPath_withAnySelection; mitk::PropertyKeyPath deepPath_withSelection; mitk::PropertyKeyPath verydeepPath; mitk::PropertyKeyPath emptyPath; public: void setUp() override { simplePath.AddElement("simple"); simplePath2.AddElement("AA-11"); deepPath.AddElement("a").AddElement("b2").AddElement("c3"); deepPath_withAnyElement.AddElement("a"); deepPath_withAnyElement.AddAnyElement(); deepPath_withAnyElement.AddElement("c3"); deepPath_withAnySelection.AddElement("a"); deepPath_withAnySelection.AddAnySelection("b"); deepPath_withAnySelection.AddElement("c"); deepPath_withSelection.AddElement("a"); deepPath_withSelection.AddSelection("b", 6); deepPath_withSelection.AddElement("c"); verydeepPath.AddAnySelection("a"); verydeepPath.AddAnyElement(); verydeepPath.AddElement("c"); verydeepPath.AddSelection("d", 4); verydeepPath.AddElement("e"); } void tearDown() override {} void AccessFunctions() { const auto constEmptyPath = emptyPath; const auto constVerydeepPath = verydeepPath; CPPUNIT_ASSERT_THROW(emptyPath.GetFirstNode(), mitk::InvalidPathNodeException); CPPUNIT_ASSERT_THROW(emptyPath.GetLastNode(), mitk::InvalidPathNodeException); CPPUNIT_ASSERT_THROW(emptyPath.GetNode(0), mitk::InvalidPathNodeException); CPPUNIT_ASSERT_THROW(constEmptyPath.GetFirstNode(), mitk::InvalidPathNodeException); CPPUNIT_ASSERT_THROW(constEmptyPath.GetLastNode(), mitk::InvalidPathNodeException); CPPUNIT_ASSERT_THROW(constEmptyPath.GetNode(0), mitk::InvalidPathNodeException); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing GetFirstNode with 'a.[*].*.c.d.[4].e'", std::string("a"), verydeepPath.GetFirstNode().name); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing const GetFirstNode with 'a.[*].*.c.d.[4].e'", std::string("a"), constVerydeepPath.GetFirstNode().name); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing GetLastNode with 'a.[*].*.c.d.[4].e'", std::string("e"), verydeepPath.GetLastNode().name); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing GetLastNode with 'a.[*].*.c.d.[4].e'", std::string("e"), constVerydeepPath.GetLastNode().name); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing GetNode(3) with 'a.[*].*.c.d.[4].e'", std::string("d"), verydeepPath.GetNode(3).name); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing GetNode(3) with 'a.[*].*.c.d.[4].e'", std::string("d"), constVerydeepPath.GetNode(3).name); CPPUNIT_ASSERT(5 == constVerydeepPath.GetSize()); CPPUNIT_ASSERT(0 == emptyPath.GetSize()); } void PropertyKeyPathToPropertyRegEx() { std::string result = mitk::PropertyKeyPathToPropertyRegEx(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyRegEx() with 'simple'", std::string("simple"), result); result = mitk::PropertyKeyPathToPropertyRegEx(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyRegEx() with 'a.b2.c3'", std::string("a\\.b2\\.c3"), result); result = mitk::PropertyKeyPathToPropertyRegEx(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyRegEx() with 'a.*.c3'", std::string("a\\.([a-zA-Z0-9- ]+)\\.c3"), result); result = mitk::PropertyKeyPathToPropertyRegEx(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyRegEx() with 'a.b.[*].c'", std::string("a\\.b\\.\\[(\\d*)\\]\\.c"), result); result = mitk::PropertyKeyPathToPropertyRegEx(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyRegEx() with 'a.b.[6].c'", std::string("a\\.b\\.\\[6\\]\\.c"), result); result = mitk::PropertyKeyPathToPropertyRegEx(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyKeyPathToPropertyRegEx() with 'a.[*].*.c.d.[4].e'", std::string("a\\.\\[(\\d*)\\]\\.([a-zA-Z0-9- ]+)\\.c\\.d\\.\\[4\\]\\.e"), result); } void PropertyKeyPathToPersistenceKeyRegEx() { std::string result = mitk::PropertyKeyPathToPersistenceKeyRegEx(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyRegEx() with 'simple'", std::string("simple"), result); result = mitk::PropertyKeyPathToPersistenceKeyRegEx(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyRegEx() with 'a.b2.c3'", std::string("a_b2_c3"), result); result = mitk::PropertyKeyPathToPersistenceKeyRegEx(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyRegEx() with 'a.*.c3'", std::string("a_([a-zA-Z0-9- ]+)_c3"), result); result = mitk::PropertyKeyPathToPersistenceKeyRegEx(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyRegEx() with 'a.b.[*].c'", std::string("a_b_\\[(\\d*)\\]_c"), result); result = mitk::PropertyKeyPathToPersistenceKeyRegEx(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyRegEx() with 'a.b.[6].c'", std::string("a_b_\\[6\\]_c"), result); result = mitk::PropertyKeyPathToPersistenceKeyRegEx(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyKeyPathToPersistenceKeyRegEx() with 'a.[*].*.c.d.[4].e'", std::string("a_\\[(\\d*)\\]_([a-zA-Z0-9- ]+)_c_d_\\[4\\]_e"), result); } void PropertyKeyPathToPersistenceKeyTemplate() { std::string result = mitk::PropertyKeyPathToPersistenceKeyTemplate(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyTemplate() with 'simple'", std::string("simple"), result); result = mitk::PropertyKeyPathToPersistenceKeyTemplate(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyTemplate() with 'a.b2.c3'", std::string("a_b2_c3"), result); result = mitk::PropertyKeyPathToPersistenceKeyTemplate(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyTemplate() with 'a.*.c3'", std::string("a_$1_c3"), result); result = mitk::PropertyKeyPathToPersistenceKeyTemplate(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyTemplate() with 'a.b.[*].c'", std::string("a_b_[$1]_c"), result); result = mitk::PropertyKeyPathToPersistenceKeyTemplate(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceKeyTemplate() with 'a.b.[6].c'", std::string("a_b_[6]_c"), result); result = mitk::PropertyKeyPathToPersistenceKeyTemplate(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyKeyPathToPersistenceKeyTemplate() with 'a.[*].*.c.d.[4].e'", std::string("a_[$1]_$2_c_d_[4]_e"), result); } void PropertyKeyPathToPersistenceNameTemplate() { std::string result = mitk::PropertyKeyPathToPersistenceNameTemplate(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceNameTemplate() with 'simple'", std::string("simple"), result); result = mitk::PropertyKeyPathToPersistenceNameTemplate(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceNameTemplate() with 'a.b2.c3'", std::string("a.b2.c3"), result); result = mitk::PropertyKeyPathToPersistenceNameTemplate(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceNameTemplate() with 'a.*.c3'", std::string("a.$1.c3"), result); result = mitk::PropertyKeyPathToPersistenceNameTemplate(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceNameTemplate() with 'a.b.[*].c'", std::string("a.b.[$1].c"), result); result = mitk::PropertyKeyPathToPersistenceNameTemplate(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPersistenceNameTemplate() with 'a.b.[6].c'", std::string("a.b.[6].c"), result); result = mitk::PropertyKeyPathToPersistenceNameTemplate(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyKeyPathToPersistenceNameTemplate() with 'a.[*].*.c.d.[4].e'", std::string("a.[$1].$2.c.d.[4].e"), result); } void PropertyNameToPropertyKeyPath() { mitk::PropertyKeyPath result = mitk::PropertyNameToPropertyKeyPath("simple"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToPropertyKeyPath() with 'simple'", simplePath, result); result = mitk::PropertyNameToPropertyKeyPath("a.b2.c3"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToPropertyKeyPath() with 'a.b2.c3'", deepPath, result); result = mitk::PropertyNameToPropertyKeyPath("a.*.c3"); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyNameToPropertyKeyPath() with 'a.*.c3'", deepPath_withAnyElement, result); result = mitk::PropertyNameToPropertyKeyPath("a.b.[*].c"); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyNameToPropertyKeyPath() with 'a.b.[*].c'", deepPath_withAnySelection, result); result = mitk::PropertyNameToPropertyKeyPath("a.b.[6].c"); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyNameToPropertyKeyPath() with 'a.b.[6].c'", deepPath_withSelection, result); result = mitk::PropertyNameToPropertyKeyPath("a.[*].*.c.d.[4].e"); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyNameToPropertyKeyPath() with 'a.[*].*.c.d.[4].e'", verydeepPath, result); result = mitk::PropertyNameToPropertyKeyPath("AA-11"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToPropertyKeyPath() with 'AA-11'", simplePath2, result); result = mitk::PropertyNameToPropertyKeyPath("$$$IlligalNameChar.sub"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToPropertyKeyPath() with wrong path", emptyPath, result); result = mitk::PropertyNameToPropertyKeyPath("emptyNode..sub"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToPropertyKeyPath() with wrong path", emptyPath, result); result = mitk::PropertyNameToPropertyKeyPath("wrongIndex.[d]"); CPPUNIT_ASSERT_EQUAL_MESSAGE("Testing PropertyNameToPropertyKeyPath() with wrong path", emptyPath, result); } void PropertyKeyPathToPropertyName() { std::string result = mitk::PropertyKeyPathToPropertyName(simplePath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyName() with 'simple'", result, std::string("simple")); result = mitk::PropertyKeyPathToPropertyName(deepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyName() with 'a.b2.c3'", result, std::string("a.b2.c3")); result = mitk::PropertyKeyPathToPropertyName(deepPath_withAnyElement); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyName() with 'a.*.c3'", result, std::string("a.*.c3")); result = mitk::PropertyKeyPathToPropertyName(deepPath_withAnySelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyName() with 'a.b.[*].c'", result, std::string("a.b.[*].c")); result = mitk::PropertyKeyPathToPropertyName(deepPath_withSelection); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyName() with 'a.b.[6].c'", result, std::string("a.b.[6].c")); result = mitk::PropertyKeyPathToPropertyName(verydeepPath); CPPUNIT_ASSERT_EQUAL_MESSAGE( "Testing PropertyKeyPathToPropertyName() with 'a.[*].*.c.d.[4].e'", result, std::string("a.[*].*.c.d.[4].e")); } void Comparison() { mitk::PropertyKeyPath deepPath_noSelection = mitk::PropertyKeyPath().AddElement("a").AddElement("b").AddElement("c"); CPPUNIT_ASSERT(deepPath_noSelection < deepPath); CPPUNIT_ASSERT(deepPath_noSelection < deepPath_withSelection); CPPUNIT_ASSERT(deepPath_withSelection < deepPath); CPPUNIT_ASSERT(deepPath_withSelection < deepPath_withAnySelection); CPPUNIT_ASSERT(deepPath_withAnyElement < deepPath_noSelection); CPPUNIT_ASSERT(!(deepPath_noSelection < deepPath_noSelection)); CPPUNIT_ASSERT(!(deepPath_noSelection > deepPath_noSelection)); CPPUNIT_ASSERT(deepPath_noSelection <= deepPath_noSelection); CPPUNIT_ASSERT(deepPath_noSelection >= deepPath_noSelection); } void ExecutePropertyRegEx() { std::regex regEx(mitk::PropertyKeyPathToPropertyRegEx(simplePath)); std::string result = mitk::PropertyKeyPathToPropertyName(simplePath); CPPUNIT_ASSERT(std::regex_match(result, regEx)); regEx = std::regex(mitk::PropertyKeyPathToPropertyRegEx(deepPath)); result = mitk::PropertyKeyPathToPropertyName(deepPath); CPPUNIT_ASSERT(std::regex_match(result, regEx)); regEx = std::regex(mitk::PropertyKeyPathToPropertyRegEx(deepPath_withAnyElement)); result = mitk::PropertyKeyPathToPropertyName(deepPath_withAnyElement); auto position = result.find("*"); if (std::string::npos != position) { result.replace(position, 1, "ConcreteNode1"); CPPUNIT_ASSERT(std::regex_match(result, regEx)); } regEx = std::regex(mitk::PropertyKeyPathToPropertyRegEx(deepPath_withAnySelection)); result = mitk::PropertyKeyPathToPropertyName(deepPath_withAnySelection); position = result.find("[*]"); if (std::string::npos != position) { result.replace(position, 3, "[10]"); CPPUNIT_ASSERT(std::regex_match(result, regEx)); } regEx = std::regex(mitk::PropertyKeyPathToPropertyRegEx(deepPath_withSelection)); result = mitk::PropertyKeyPathToPropertyName(deepPath_withSelection); CPPUNIT_ASSERT(std::regex_match(result, regEx)); regEx = std::regex(mitk::PropertyKeyPathToPropertyRegEx(verydeepPath)); result = mitk::PropertyKeyPathToPropertyName(verydeepPath); position = result.find("[*]"); if (std::string::npos != position) { result.replace(position, 3, "[1]"); position = result.find("*"); if (std::string::npos != position) { result.replace(position, 1, "ConcreteNode2"); CPPUNIT_ASSERT(std::regex_match(result, regEx)); } } } + + void InitializerList() + { + mitk::PropertyKeyPath newPath = {"a","b2","c3"}; + CPPUNIT_ASSERT(newPath == deepPath); + } }; MITK_TEST_SUITE_REGISTRATION(mitkPropertyKeyPath) diff --git a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp index 04768c9a2d..1be7a6b47e 100644 --- a/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp +++ b/Modules/Core/test/mitkPropertyRelationRuleBaseTest.cpp @@ -1,915 +1,1101 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPropertyRelationRuleBase.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include /** This class is used to test PropertyRelationRuleBase and get access to internals where needed to test them as well. */ namespace mitk { class TestRule : public mitk::PropertyRelationRuleBase { public: mitkClassMacro(TestRule, mitk::PropertyRelationRuleBase); itkFactorylessNewMacro(Self); itkCloneMacro(Self); using RuleIDType = PropertyRelationRuleBase::RuleIDType; using RelationUIDType = PropertyRelationRuleBase::RelationUIDType; using RelationUIDVectorType = PropertyRelationRuleBase::RelationUIDVectorType; RuleIDType GetRuleID() const override { if (m_AbstractMode) { return "TestRule"; } else { return "TestRule_type1"; } }; std::string GetDisplayName() const override { return "TestDisplayName"; } std::string GetSourceRoleName() const override { return "source role"; } std::string GetDestinationRoleName() const override { return "destination role"; } bool m_AbstractMode; bool IsAbstract() const override { return m_AbstractMode; } using Superclass::GetRootKeyPath; using Superclass::Connect; protected: TestRule() : m_AbstractMode(false) { }; ~TestRule() override = default; using InstanceIDType = PropertyRelationRuleBase::InstanceIDType; using InstanceIDVectorType = PropertyRelationRuleBase::InstanceIDVectorType; bool IsSupportedRuleID(const RuleIDType& ruleID) const override { if (m_AbstractMode) { return ruleID.find(this->GetRuleID()) == 0; } else { return Superclass::IsSupportedRuleID(ruleID); } }; - InstanceIDVectorType GetInstanceID_datalayer(const IPropertyProvider *source, - const IPropertyProvider *destination) const override + DataRelationUIDVectorType GetRelationUIDs_DataLayer(const IPropertyProvider* source, + const IPropertyProvider* destination, const InstanceIDVectorType& instances_IDLayer) const override { - InstanceIDVectorType result; + DataRelationUIDVectorType result; - auto destProp = destination->GetConstProperty("name"); + mitk::BaseProperty::ConstPointer destProp; + + if (destination != nullptr) + { + destProp = destination->GetConstProperty("name"); + } if (destProp.IsNotNull()) { auto destRegExStr = PropertyKeyPathToPropertyRegEx(Superclass::GetRootKeyPath().AddAnyElement().AddElement("dataHandle")); auto regEx = std::regex(destRegExStr); std::smatch instance_matches; auto keys = source->GetPropertyKeys(); for (const auto &key : keys) { if (std::regex_search(key, instance_matches, regEx)) { auto sourceProp = source->GetConstProperty(key); if (sourceProp->GetValueAsString() == destProp->GetValueAsString()) { if (instance_matches.size()>1) { - result.push_back(instance_matches[1]); + auto finding = std::find(instances_IDLayer.begin(), instances_IDLayer.end(), instance_matches[1]); + if (finding == instances_IDLayer.end()) + { + result.emplace_back(this->GetRelationUIDByInstanceID(source, instance_matches[1]), this->GetRuleIDByInstanceID(source, instance_matches[1])); + } } } } } } - return result; - }; - bool HasImplicitDataRelation(const IPropertyProvider *source, - const IPropertyProvider *destination) const override - { - auto destProp = destination->GetConstProperty("name"); - auto sourceProp = source->GetConstProperty("referencedName"); + if (result.empty() && instances_IDLayer.empty()) + { + auto refNameProp = source->GetConstProperty("referencedName"); + if (refNameProp.IsNotNull() && (destProp.IsNull() || destProp->GetValueAsString() == refNameProp->GetValueAsString())) + { + result.emplace_back(refNameProp->GetValueAsString(),""); + } + } - return destProp.IsNotNull() && sourceProp.IsNotNull() && - destProp->GetValueAsString() == sourceProp->GetValueAsString(); + return result; }; void Connect_datalayer(IPropertyOwner *source, const IPropertyProvider *destination, const InstanceIDType &instanceID) const override { auto destProp = destination->GetConstProperty("name"); source->SetProperty("referencedName", StringProperty::New(destProp->GetValueAsString())); source->SetProperty( PropertyKeyPathToPropertyName(Superclass::GetRootKeyPath().AddElement(instanceID).AddElement("dataHandle")), StringProperty::New(destProp->GetValueAsString())); }; - void Disconnect_datalayer(IPropertyOwner *source, const InstanceIDType & /*instanceID*/) const override + void Disconnect_datalayer(IPropertyOwner *source, const RelationUIDType & relationUID) const override { source->RemoveProperty("referencedName"); + try + { + auto instanceID = this->GetInstanceIDByRelationUID(source, relationUID); + source->RemoveProperty( + PropertyKeyPathToPropertyName(Superclass::GetRootKeyPath().AddElement(instanceID).AddElement("dataHandle"))); + } + catch(...) + { } }; }; } // namespace mitk class mitkPropertyRelationRuleBaseTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkPropertyRelationRuleBaseTestSuite); MITK_TEST(GetRootKeyPath); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); MITK_TEST(GetDestinationsDetector); MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); + MITK_TEST(Disconnect_partial_ID); + MITK_TEST(Disconnect_partial_Data); MITK_TEST(Connect_abstract); MITK_TEST(Disconnect_abstract); CPPUNIT_TEST_SUITE_END(); private: mitk::TestRule::Pointer rule; mitk::TestRule::Pointer abstractRule; mitk::DataNode::Pointer unRelated; mitk::PointSet::Pointer unRelated_1_data; mitk::DataNode::Pointer source_implicit_1; mitk::DataNode::Pointer source_data_1; mitk::DataNode::Pointer source_idOnly_1; mitk::DataNode::Pointer source_1; mitk::DataNode::Pointer source_multi; mitk::DataNode::Pointer source_otherRule; mitk::DataNode::Pointer source_otherTypeRule; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1; mitk::PointSet::Pointer dest_1_data; mitk::DataNode::Pointer dest_2; mitk::PointSet::Pointer dest_2_data; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } public: void setUp() override { rule = mitk::TestRule::New(); abstractRule = mitk::TestRule::New(); abstractRule->m_AbstractMode = true; unRelated = mitk::DataNode::New(); unRelated->SetName("unRelated"); unRelated_1_data = mitk::PointSet::New(); unRelated->SetData(unRelated_1_data); dest_1 = mitk::DataNode::New(); dest_1->SetName("dest_1"); dest_1_data = mitk::PointSet::New(); dest_1->SetData(dest_1_data); dest_2 = mitk::DataNode::New(); dest_2->SetName("dest_2"); dest_2_data = mitk::PointSet::New(); dest_2->SetData(dest_2_data); source_implicit_1 = mitk::DataNode::New(); source_implicit_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); source_idOnly_1 = mitk::DataNode::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); - source_data_1 = source_implicit_1->Clone(); + source_data_1 = mitk::DataNode::New(); + source_data_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.dataHandle"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid10"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_data_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); - source_1 = source_data_1->Clone(); + source_1 = mitk::DataNode::New(); + source_1->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid3"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); + name = "MITK.Relations.1.dataHandle"; + source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("uid8"), nullptr, true); name = "MITK.Relations.2.destinationUID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); - source_multi = source_1->Clone(); + source_multi = mitk::DataNode::New(); + source_multi->AddProperty("referencedName", mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.1.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid4"), nullptr, true); name = "MITK.Relations.1.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); + name = "MITK.Relations.1.dataHandle"; + source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetName())); name = "MITK.Relations.4.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid5")); name = "MITK.Relations.4.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(dest_2_data->GetUID())); name = "MITK.Relations.4.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.2.relationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("uid6")); name = "MITK.Relations.2.destinationUID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New("unknown")); name = "MITK.Relations.2.ruleID"; source_multi->AddProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_otherRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->AddProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherTypeRule = mitk::DataNode::New(); name = "MITK.Relations.1.relationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New(dest_1_data->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherTypeRule->AddProperty(name.c_str(), mitk::StringProperty::New("TestRule_othertype")); } void tearDown() override {} void GetRootKeyPath() { mitk::PropertyKeyPath ref; ref.AddElement("MITK").AddElement("Relations"); CPPUNIT_ASSERT(mitk::PropertyRelationRuleBase::GetRootKeyPath() == ref); } void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); - CPPUNIT_ASSERT(!rule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(rule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(rule->IsSource(source_data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); CPPUNIT_ASSERT(rule->IsSource(source_multi)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherTypeRule)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); - CPPUNIT_ASSERT(!abstractRule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_multi)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherTypeRule)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_otherTypeRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated)); + CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1)); + CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1)); + CPPUNIT_ASSERT(!rule->HasRelation(source_otherTypeRule, dest_1)); + + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1)); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2)); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2)); + CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, unRelated)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(unRelated, dest_1)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherRule, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherTypeRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherTypeRule).empty()); - CPPUNIT_ASSERT(rule->GetExistingRelations(source_implicit_1).empty()); - auto uids = rule->GetExistingRelations(source_idOnly_1); + auto uids = rule->GetExistingRelations(source_implicit_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == dest_1->GetName()); + + uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = rule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); uids = rule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); - CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_implicit_1).empty()); + + uids = abstractRule->GetExistingRelations(source_implicit_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == dest_1->GetName()); uids = abstractRule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); uids = abstractRule->GetExistingRelations(source_data_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); uids = abstractRule->GetExistingRelations(source_multi); CPPUNIT_ASSERT(uids.size() == 3); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid4") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid5") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid6") != uids.end()); uids = abstractRule->GetExistingRelations(source_otherTypeRule); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid9"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherTypeRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherTypeRule, dest_1).front() == "uid9"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); uids = abstractRule->GetRelationUIDs(source_data_1, dest_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_2); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, dest_1).front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_1).front() == "uid4"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_multi, dest_2).front() == "uid5"); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); auto predicate2 = abstractRule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated)); - CPPUNIT_ASSERT(!predicate2->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_1)); CPPUNIT_ASSERT(predicate2->CheckNode(source_multi)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate2->CheckNode(source_otherTypeRule)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); - predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); - CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); - predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); - predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); + + CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); + + predicate = rule->GetSourcesDetector(dest_2); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); - predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); + predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); + + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); + + predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherTypeRule)); + + CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1)); + CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); + predicate = abstractRule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherTypeRule)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_data_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_1)); CPPUNIT_ASSERT(predicate->CheckNode(source_multi)); } void GetDestinationsDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationsDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetDestinationsDetector(source_otherRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_implicit_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_data_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_idOnly_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + predicate = + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Complete); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); predicate = rule->GetDestinationsDetector(source_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); + predicate = + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationsDetector(source_multi); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); predicate = - rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); - CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = - rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); + predicate = + rule->GetDestinationsDetector(source_multi, mitk::PropertyRelationRuleBase::RelationType::Complete); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); predicate = abstractRule->GetDestinationsDetector(source_otherTypeRule); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); } void GetDestinationDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationDetector(nullptr, "uid1"), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", rule->GetDestinationDetector(source_1, "invalid uid"), itk::ExceptionObject); auto predicate = rule->GetDestinationDetector(source_1, "uid3"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2)); predicate = rule->GetDestinationDetector(source_multi, "uid5"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1)); CPPUNIT_ASSERT(predicate->CheckNode(dest_2)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_implicit_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); // check upgrade of an data connection - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_data_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); std::string name = "MITK.Relations.1.destinationUID"; auto prop = source_data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1_data->GetUID()); // check actualization of an id only connection + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Connect(source_idOnly_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.dataHandle"; prop = source_idOnly_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information not stored.", prop->GetValueAsString() == dest_1->GetName()); prop = source_idOnly_1->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == dest_1->GetName()); // check actualization of an existing connection rule->Connect(source_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); // check new connection auto newConnectUID = rule->Connect(source_multi, unRelated); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, unRelated) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, unRelated, mitk::PropertyRelationRuleBase::RelationType::Complete)); name = "MITK.Relations.5.dataHandle"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); prop = source_multi->GetProperty("referencedName"); CPPUNIT_ASSERT_MESSAGE( "Data layer information was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect data layer information was stored.", prop->GetValueAsString() == unRelated->GetName()); name = "MITK.Relations.5.destinationUID"; prop = source_multi->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated_1_data->GetUID()); auto storedRelationUIDs = rule->GetRelationUIDs(source_multi, unRelated); CPPUNIT_ASSERT_MESSAGE( "Relation uid was not stored for given source and destination.", storedRelationUIDs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Incorrect Relation uid was stored.", storedRelationUIDs[0] == newConnectUID); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT_MESSAGE("Data property was not removed.", !source_1->GetProperty("referencedName")); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); rule->Disconnect(source_1, dest_2); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(this->hasRelationProperties(source_1)); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.",this->hasRelationProperties(source_1, "2")); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); rule->Disconnect(source_multi, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); rule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", this->hasRelationProperties(source_otherTypeRule, "1")); } + void Disconnect_partial_ID() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->Disconnect(nullptr, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(source_1, nullptr, mitk::PropertyRelationRuleBase::RelationType::ID), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(nullptr, "uid", mitk::PropertyRelationRuleBase::RelationType::ID), + itk::ExceptionObject); + + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + rule->Disconnect(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); + + rule->Disconnect(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + + rule->Disconnect(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + rule->Disconnect(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); + CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", this->hasRelationProperties(source_1, "2")); + } + + void Disconnect_partial_Data() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", + rule->Disconnect(nullptr, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(source_1, nullptr, mitk::PropertyRelationRuleBase::RelationType::Data), + itk::ExceptionObject); + + CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", + rule->Disconnect(nullptr, "uid", mitk::PropertyRelationRuleBase::RelationType::Data), + itk::ExceptionObject); + + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + rule->Disconnect(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT_MESSAGE("Data property was illegaly removed.", source_1->GetProperty("referencedName")); + + rule->Disconnect(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + + rule->Disconnect(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(!rule->HasRelation(source_data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + rule->Disconnect(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + } + void Connect_abstract() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(source_1, nullptr), itk::ExceptionObject); } void Disconnect_abstract() { - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::ID)); abstractRule->Disconnect(source_1, dest_2); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_1); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_multi, dest_1); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_multi, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_multi, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "uid6"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_multi, "unkownRelationUID"); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "1")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_multi, "2")); CPPUNIT_ASSERT(this->hasRelationProperties(source_multi, "4")); abstractRule->Disconnect(source_otherTypeRule, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherTypeRule, "1")); - } }; MITK_TEST_SUITE_REGISTRATION(mitkPropertyRelationRuleBase) diff --git a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp index 2494c9c792..006153952b 100644 --- a/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp +++ b/Modules/Core/test/mitkSourceImageRelationRuleTest.cpp @@ -1,880 +1,944 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkSourceImageRelationRule.h" #include "mitkDataNode.h" #include "mitkPointSet.h" #include "mitkStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include "mitkPropertyNameHelper.h" #include "mitkTemporoSpatialStringProperty.h" #include "mitkPropertyNameHelper.h" #include class mitkSourceImageRelationRuleTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkSourceImageRelationRuleTestSuite); MITK_TEST(IsSourceCandidate); MITK_TEST(IsDestinationCandidate); MITK_TEST(IsSource); MITK_TEST(HasRelation); MITK_TEST(GetExistingRelations); MITK_TEST(GetRelationUIDs); MITK_TEST(GetSourceCandidateIndicator); MITK_TEST(GetDestinationCandidateIndicator); MITK_TEST(GetConnectedSourcesDetector); MITK_TEST(GetSourcesDetector); MITK_TEST(GetDestinationsDetector); MITK_TEST(GetDestinationDetector); MITK_TEST(Connect); MITK_TEST(Disconnect); MITK_TEST(Connect_abstract); MITK_TEST(Disconnect_abstract); CPPUNIT_TEST_SUITE_END(); private: mitk::SourceImageRelationRule::Pointer rule; mitk::SourceImageRelationRule::Pointer abstractRule; mitk::Image::Pointer unRelated; mitk::DataNode::Pointer unRelated_Node; mitk::Image::Pointer source_implicit_1; mitk::DataNode::Pointer source_implicit_1_Node; - mitk::Image::Pointer source_data_1; - mitk::DataNode::Pointer source_data_1_Node; + mitk::Image::Pointer source_Data_1; + mitk::DataNode::Pointer source_Data_1_Node; mitk::Image::Pointer source_idOnly_1; mitk::DataNode::Pointer source_idOnly_1_Node; mitk::Image::Pointer source_1; mitk::DataNode::Pointer source_1_Node; mitk::Image::Pointer source_otherRule; mitk::DataNode::Pointer source_otherRule_Node; mitk::Image::Pointer source_otherPurpose; mitk::DataNode::Pointer source_otherPurpose_Node; //relevant for abstract rule checks. Abstract rule should see it concrete rule not. mitk::DataNode::Pointer dest_1_Node; mitk::Image::Pointer dest_1; mitk::DataNode::Pointer dest_2_Node; mitk::Image::Pointer dest_2; bool hasRelationProperties(mitk::IPropertyProvider *provider, std::string instance = "") const { auto keyPath = mitk::PropertyRelationRuleBase::GetRootKeyPath(); if (!instance.empty()) { keyPath.AddElement(instance); } auto prefix = mitk::PropertyKeyPathToPropertyName(keyPath); auto keys = provider->GetPropertyKeys(); for (const auto &key : keys) { if (key.find(prefix) == 0) { return true; } } return false; } std::vector GetReferenceSequenceIndices(const mitk::IPropertyProvider * source, const mitk::IPropertyProvider * destination) const { std::vector result; auto destInstanceUIDProp = destination->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018)); if (destInstanceUIDProp.IsNull()) { return result; } mitk::PropertyKeyPath referencedInstanceUIDs; referencedInstanceUIDs.AddElement("DICOM").AddElement("0008").AddAnySelection("2112").AddElement("0008").AddElement("1155"); auto sourceRegExStr = PropertyKeyPathToPropertyRegEx(referencedInstanceUIDs);; auto regEx = std::regex(sourceRegExStr); std::vector keys; //workaround until T24729 is done. Please remove if T24728 is done keys = source->GetPropertyKeys(); //end workaround for T24729 for (const auto &key : keys) { if (std::regex_match(key, regEx)) { auto refUIDProp = source->GetConstProperty(key); if (*refUIDProp == *destInstanceUIDProp) { mitk::PropertyKeyPath finding = mitk::PropertyNameToPropertyKeyPath(key); result.push_back(std::to_string(finding.GetNode(2).selection)); } } } return result; }; void SetDICOMReferenceInfo(mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); owner->SetProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath), mitk::TemporoSpatialStringProperty::New(instanceUID)); mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); owner->SetProperty(PropertyKeyPathToPropertyName(refClassUIDPath), mitk::TemporoSpatialStringProperty::New(classUID)); mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); owner->SetProperty(PropertyKeyPathToPropertyName(purposePath), mitk::TemporoSpatialStringProperty::New(purpose)); } bool IsCorrectDICOMReference(const mitk::IPropertyOwner* owner, const std::string& instanceUID, const std::string& classUID, const std::string& purpose, unsigned int sequElement) const { mitk::PropertyKeyPath refInstanceUIDPath; refInstanceUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1155"); auto prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refInstanceUIDPath)); if (prop->GetValueAsString() != instanceUID) { return false; } mitk::PropertyKeyPath refClassUIDPath; refClassUIDPath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0008").AddElement("1150"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(refClassUIDPath)); if (prop->GetValueAsString() != classUID) { return false; } mitk::PropertyKeyPath purposePath; purposePath.AddElement("DICOM").AddElement("0008").AddSelection("2112", sequElement).AddElement("0040").AddSelection("a170", 0).AddElement("0008").AddElement("0104"); prop = owner->GetConstProperty(PropertyKeyPathToPropertyName(purposePath)); if (prop->GetValueAsString() != purpose) { return false; } return true; } public: void setUp() override { auto instanceUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0018); auto classUIDPropName = mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0016); rule = mitk::SourceImageRelationRule::New("Test"); abstractRule = mitk::SourceImageRelationRule::New(); unRelated = mitk::Image::New(); unRelated->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("unRelated")); unRelated->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); unRelated_Node = mitk::DataNode::New(); unRelated_Node->SetData(unRelated); dest_1_Node = mitk::DataNode::New(); dest_1_Node->SetName("dest_1"); dest_1 = mitk::Image::New(); dest_1->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_1")); dest_1->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); dest_1_Node->SetData(dest_1); dest_2_Node = mitk::DataNode::New(); dest_2_Node->SetName("dest_2"); dest_2 = mitk::Image::New(); dest_2->SetProperty(instanceUIDPropName, mitk::TemporoSpatialStringProperty::New("dest_2")); dest_2->SetProperty(classUIDPropName, mitk::TemporoSpatialStringProperty::New("image")); dest_2_Node->SetData(dest_2); source_implicit_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_implicit_1, "dest_1", "image", "Test", 0); source_implicit_1_Node = mitk::DataNode::New(); source_implicit_1_Node->SetData(source_implicit_1); source_idOnly_1 = mitk::Image::New(); std::string name = "MITK.Relations.1.relationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid1")); name = "MITK.Relations.1.destinationUID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_idOnly_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); source_idOnly_1_Node = mitk::DataNode::New(); source_idOnly_1_Node->SetData(source_idOnly_1); - source_data_1 = mitk::Image::New(); - SetDICOMReferenceInfo(source_data_1, "dest_1", "image", "Test", 0); - SetDICOMReferenceInfo(source_data_1, "dest_2", "image", "otherpurpose", 1); + source_Data_1 = mitk::Image::New(); + SetDICOMReferenceInfo(source_Data_1, "dest_1", "image", "Test", 0); + SetDICOMReferenceInfo(source_Data_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2")); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid2")); name = "MITK.Relations.1.ruleID"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10")); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid10")); name = "MITK.Relations.2.SourceImageSequenceItem"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); name = "MITK.Relations.2.ruleID"; - source_data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); - source_data_1_Node = mitk::DataNode::New(); - source_data_1_Node->SetData(source_data_1); + source_Data_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); + source_Data_1_Node = mitk::DataNode::New(); + source_Data_1_Node->SetData(source_Data_1); source_1 = mitk::Image::New(); SetDICOMReferenceInfo(source_1, "dest_1", "image", "Test", 0); SetDICOMReferenceInfo(source_1, "dest_2", "image", "otherpurpose", 1); name = "MITK.Relations.1.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid3")); name = "MITK.Relations.1.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(rule->GetRuleID())); name = "MITK.Relations.1.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("0")); name = "MITK.Relations.2.relationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("uid8")); name = "MITK.Relations.2.destinationUID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New(dest_2->GetUID())); name = "MITK.Relations.2.ruleID"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); name = "MITK.Relations.2.SourceImageSequenceItem"; source_1->SetProperty(name.c_str(), mitk::StringProperty::New("1")); source_1_Node = mitk::DataNode::New(); source_1_Node->SetData(source_1); source_otherRule = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("uid7")); name = "MITK.Relations.1.destinationUID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherRule->SetProperty(name.c_str(), mitk::StringProperty::New("otherRuleID")); source_otherRule_Node = mitk::DataNode::New(); source_otherRule_Node->SetData(source_otherRule); source_otherPurpose = mitk::Image::New(); name = "MITK.Relations.1.relationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("uid9")); name = "MITK.Relations.1.destinationUID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New(dest_1->GetUID())); name = "MITK.Relations.1.ruleID"; source_otherPurpose->SetProperty(name.c_str(), mitk::StringProperty::New("SourceImageRelation otherpurpose")); source_otherPurpose_Node = mitk::DataNode::New(); source_otherPurpose_Node->SetData(source_otherPurpose); } void tearDown() override {} void IsSourceCandidate() { CPPUNIT_ASSERT(rule->IsSourceCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsSourceCandidate(nullptr)); } void IsDestinationCandidate() { CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1_Node)); CPPUNIT_ASSERT(rule->IsDestinationCandidate(this->dest_1)); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(mitk::DataNode::New())); CPPUNIT_ASSERT(!rule->IsDestinationCandidate(nullptr)); } void IsSource() { CPPUNIT_ASSERT_THROW_MESSAGE( "Violated precondition (nullptr) does not throw.", rule->IsSource(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(!rule->IsSource(unRelated)); - CPPUNIT_ASSERT(!rule->IsSource(source_implicit_1)); - CPPUNIT_ASSERT(rule->IsSource(source_data_1)); + CPPUNIT_ASSERT(rule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(rule->IsSource(source_Data_1)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(rule->IsSource(source_1)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose)); - CPPUNIT_ASSERT(!rule->IsSource(source_implicit_1_Node)); - CPPUNIT_ASSERT(rule->IsSource(source_data_1_Node)); + CPPUNIT_ASSERT(rule->IsSource(source_implicit_1_Node)); + CPPUNIT_ASSERT(rule->IsSource(source_Data_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_idOnly_1_Node)); CPPUNIT_ASSERT(rule->IsSource(source_1_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherRule_Node)); CPPUNIT_ASSERT(!rule->IsSource(source_otherPurpose_Node)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated)); - CPPUNIT_ASSERT(!abstractRule->IsSource(source_implicit_1)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose)); CPPUNIT_ASSERT(!abstractRule->IsSource(unRelated_Node)); - CPPUNIT_ASSERT(!abstractRule->IsSource(source_implicit_1_Node)); - CPPUNIT_ASSERT(abstractRule->IsSource(source_data_1_Node)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_implicit_1_Node)); + CPPUNIT_ASSERT(abstractRule->IsSource(source_Data_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_idOnly_1_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_1_Node)); CPPUNIT_ASSERT(!abstractRule->IsSource(source_otherRule_Node)); CPPUNIT_ASSERT(abstractRule->IsSource(source_otherPurpose_Node)); } void HasRelation() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->HasRelation(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->HasRelation(source_1, nullptr), itk::ExceptionObject); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(rule->HasRelation(source_otherPurpose, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); - - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherRule, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); - - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!rule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!rule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); + + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherRule, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_otherPurpose, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::ID)); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); + + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); } void GetExistingRelations() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetExistingRelations(nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherRule).empty()); CPPUNIT_ASSERT(rule->GetExistingRelations(source_otherPurpose).empty()); - CPPUNIT_ASSERT(rule->GetExistingRelations(source_implicit_1).empty()); - auto uids = rule->GetExistingRelations(source_idOnly_1); + auto uids = rule->GetExistingRelations(source_implicit_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); + + uids = rule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); - uids = rule->GetExistingRelations(source_data_1); + uids = rule->GetExistingRelations(source_Data_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid2"); uids = rule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); - CPPUNIT_ASSERT(abstractRule->GetExistingRelations(unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_otherRule).empty()); - CPPUNIT_ASSERT(abstractRule->GetExistingRelations(source_implicit_1).empty()); + + uids = abstractRule->GetExistingRelations(source_implicit_1); + CPPUNIT_ASSERT(uids.size() == 1); + CPPUNIT_ASSERT(uids.front() == "DICOM.0008.2112.[0].0008.1155"); uids = abstractRule->GetExistingRelations(source_idOnly_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid1"); - uids = abstractRule->GetExistingRelations(source_data_1); + uids = abstractRule->GetExistingRelations(source_Data_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid10") != uids.end()); uids = abstractRule->GetExistingRelations(source_1); CPPUNIT_ASSERT(uids.size() == 2); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); uids = abstractRule->GetExistingRelations(source_otherPurpose); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid9"); } void GetRelationUIDs() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetRelationUIDs(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetRelationUIDs(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_1, dest_2).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_otherPurpose, dest_1).empty()); CPPUNIT_ASSERT(rule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); - CPPUNIT_ASSERT(rule->GetRelationUIDs(source_data_1, dest_1).front() == "uid2"); + CPPUNIT_ASSERT(rule->GetRelationUIDs(source_Data_1, dest_1).front() == "uid2"); auto uids = rule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(uids.front() == "uid3"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_1, unRelated).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(unRelated, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherRule, dest_1).empty()); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_otherPurpose, dest_1).front() == "uid9"); CPPUNIT_ASSERT(abstractRule->GetRelationUIDs(source_idOnly_1, dest_1).front() == "uid1"); - uids = abstractRule->GetRelationUIDs(source_data_1, dest_1); + uids = abstractRule->GetRelationUIDs(source_Data_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid2") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_1); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid3") != uids.end()); uids = abstractRule->GetRelationUIDs(source_1, dest_2); CPPUNIT_ASSERT(uids.size() == 1); CPPUNIT_ASSERT(std::find(uids.begin(), uids.end(), "uid8") != uids.end()); } void GetSourceCandidateIndicator() { auto predicate = rule->GetSourceCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetDestinationCandidateIndicator() { auto predicate = rule->GetDestinationCandidateIndicator(); CPPUNIT_ASSERT(predicate->CheckNode(this->dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(mitk::DataNode::New())); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); } void GetConnectedSourcesDetector() { auto predicate = rule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); auto predicate2 = abstractRule->GetConnectedSourcesDetector(); CPPUNIT_ASSERT(!predicate2->CheckNode(nullptr)); CPPUNIT_ASSERT(!predicate2->CheckNode(unRelated_Node)); - CPPUNIT_ASSERT(!predicate2->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate2->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate2->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_1_Node)); CPPUNIT_ASSERT(!predicate2->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate2->CheckNode(source_otherPurpose_Node)); } void GetSourcesDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->GetSourcesDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); - predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Data); + + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); + + CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); + + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); - predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + predicate = rule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); - predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + predicate = rule->GetSourcesDetector(dest_2, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_1_Node)); - predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + predicate = abstractRule->GetSourcesDetector(dest_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(!predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); predicate = abstractRule->GetSourcesDetector(dest_1); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(source_otherRule_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_otherPurpose_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_implicit_1_Node)); - CPPUNIT_ASSERT(predicate->CheckNode(source_data_1_Node)); + CPPUNIT_ASSERT(predicate->CheckNode(source_Data_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_idOnly_1_Node)); CPPUNIT_ASSERT(predicate->CheckNode(source_1_Node)); } void GetDestinationsDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationsDetector(nullptr), itk::ExceptionObject); auto predicate = rule->GetDestinationsDetector(source_otherRule); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_implicit_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_implicit_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); - predicate = rule->GetDestinationsDetector(source_data_1); + predicate = rule->GetDestinationsDetector(source_Data_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Data); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_data_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::ID); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_Data_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_idOnly_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = - rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + predicate = + rule->GetDestinationsDetector(source_idOnly_1, mitk::PropertyRelationRuleBase::RelationType::Complete); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_1_Node)); predicate = rule->GetDestinationsDetector(source_1); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_Data); + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Data); + CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); + CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); + predicate = + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::ID); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = - rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + rule->GetDestinationsDetector(source_1, mitk::PropertyRelationRuleBase::RelationType::Complete); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); predicate = abstractRule->GetDestinationsDetector(source_otherPurpose); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); } void GetDestinationDetector() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->GetDestinationDetector(nullptr, "uid1"), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (relation uid is invalid) does not throw.", rule->GetDestinationDetector(source_1, "invalid uid"), itk::ExceptionObject); auto predicate = rule->GetDestinationDetector(source_1, "uid3"); CPPUNIT_ASSERT(!predicate->CheckNode(unRelated_Node)); CPPUNIT_ASSERT(predicate->CheckNode(dest_1_Node)); CPPUNIT_ASSERT(!predicate->CheckNode(dest_2_Node)); } void Connect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Connect(source_1, nullptr), itk::ExceptionObject); // check upgrade of an implicit connection - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Implicit_Data); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); rule->Connect(source_implicit_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_implicit_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); auto dcmRefs = GetReferenceSequenceIndices(source_implicit_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_implicit_1, "dest_1", "image", "Test", 0)); - // check upgrade and reuse of an data connection (no new relation should be generated). - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_Data); - rule->Connect(source_data_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_data_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + // check upgrade and reuse of an Data connection (no new relation should be generated). + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Data)); + rule->Connect(source_Data_1, dest_1); + CPPUNIT_ASSERT(rule->HasRelation(source_Data_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); - auto relUID = rule->GetRelationUIDs(source_data_1, dest_1); + auto relUID = rule->GetRelationUIDs(source_Data_1, dest_1); CPPUNIT_ASSERT(relUID.size() == 1); std::string name = "MITK.Relations.1.destinationUID"; - auto prop = source_data_1->GetProperty(name.c_str()); + auto prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; - prop = source_data_1->GetProperty(name.c_str()); + prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; - prop = source_data_1->GetProperty(name.c_str()); + prop = source_Data_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); - dcmRefs = GetReferenceSequenceIndices(source_data_1, dest_1); + dcmRefs = GetReferenceSequenceIndices(source_Data_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); - CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_data_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_Data_1, "dest_1", "image", "Test", 0)); // check actualization of an id only connection rule->Connect(source_idOnly_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1) == - mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_idOnly_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); // check actualization of an existing connection rule->Connect(source_1, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was defined instead of updating exting one.", rule->GetExistingRelations(source_1).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(source_1, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); // check creation of an new connection rule->Connect(unRelated, dest_1); - CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Relation was not defined instead of updating exting one.", rule->GetExistingRelations(unRelated).size() == 1); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_1); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was defined instead of using the existing one.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference squence is corrupted. Should be just an index 0.", dcmRefs[0] == "0"); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); // check creation of a 2nd connection of the same purpose rule->Connect(unRelated, dest_2); - CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(rule->HasRelation(unRelated, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); CPPUNIT_ASSERT_MESSAGE("Additional relation was not defined.", rule->GetExistingRelations(unRelated).size() == 2); name = "MITK.Relations.1.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_1->GetUID()); name = "MITK.Relations.1.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.1.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "0"); name = "MITK.Relations.2.destinationUID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == dest_2->GetUID()); name = "MITK.Relations.2.ruleID"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect ruleID was stored.", prop->GetValueAsString() == rule->GetRuleID()); name = "MITK.Relations.2.SourceImageSequenceItem"; prop = unRelated->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == "1"); dcmRefs = GetReferenceSequenceIndices(unRelated, dest_2); CPPUNIT_ASSERT_MESSAGE("Additional dicom reference was not defined.", dcmRefs.size() == 1); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_1", "image", "Test", 0)); CPPUNIT_ASSERT_MESSAGE("Dicom reference is not correct.", IsCorrectDICOMReference(unRelated, "dest_2", "image", "Test", 1)); } void Disconnect() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (source is nullptr) does not throw.", rule->Disconnect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(source_1, nullptr), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (destination is nullptr) does not throw.", rule->Disconnect(nullptr, "uid"), itk::ExceptionObject); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); rule->Disconnect(source_1, unRelated); - CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated) == mitk::PropertyRelationRuleBase::RelationType::None); - CPPUNIT_ASSERT_MESSAGE("Other relationdata property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT_MESSAGE("Other relationData property was removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); //check if index correction is correct, when disconnecting rule->Connect(source_1, dest_2); rule->Connect(source_1, unRelated); rule->Disconnect(source_1, dest_2); - CPPUNIT_ASSERT(rule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!rule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); + CPPUNIT_ASSERT(rule->HasRelation(source_1, unRelated, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "1")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "2")); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "3")); CPPUNIT_ASSERT(this->hasRelationProperties(source_1, "4")); CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_1 has been removed.", IsCorrectDICOMReference(source_1, "dest_1", "image", "Test", 0)); CPPUNIT_ASSERT_MESSAGE("Dicom reference to dest_2 (other purpose) has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "dest_2", "image", "otherpurpose", 1)); CPPUNIT_ASSERT_MESSAGE("Dicom reference to unRelated has been removed or has not a corrected sequence index (1 instead of 2).", IsCorrectDICOMReference(source_1, "unRelated", "image", "Test", 2)); std::string name = "MITK.Relations.4.destinationUID"; auto prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE( "Destination uid was not stored with the correct key. Already existing session should be used.", prop); CPPUNIT_ASSERT_MESSAGE("Incorrect destination uid was stored.", prop->GetValueAsString() == unRelated->GetUID()); name = "MITK.Relations.4.SourceImageSequenceItem"; prop = source_1->GetProperty(name.c_str()); CPPUNIT_ASSERT_MESSAGE("SourceImageSequenceItem was not actualized correctly.", prop->GetValueAsString() == "2"); rule->Disconnect(source_otherPurpose, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule purpose was removed.", this->hasRelationProperties(source_otherPurpose, "1")); } void Connect_abstract() { CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(nullptr, dest_1), itk::ExceptionObject); CPPUNIT_ASSERT_THROW_MESSAGE("Violated precondition (abstract does not connect) does not throw.", abstractRule->Connect(source_1, nullptr), itk::ExceptionObject); } void Disconnect_abstract() { - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_2); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_2) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_2, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "2")); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::Connected_ID); + CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::Complete)); abstractRule->Disconnect(source_1, dest_1); - CPPUNIT_ASSERT(abstractRule->HasRelation(source_1, dest_1) == mitk::PropertyRelationRuleBase::RelationType::None); + CPPUNIT_ASSERT(!abstractRule->HasRelation(source_1, dest_1, mitk::PropertyRelationRuleBase::RelationType::None)); CPPUNIT_ASSERT(!this->hasRelationProperties(source_1, "1")); abstractRule->Disconnect(source_otherPurpose, dest_1); CPPUNIT_ASSERT_MESSAGE("Data of other rule type was removed.", !this->hasRelationProperties(source_otherPurpose, "1")); } }; MITK_TEST_SUITE_REGISTRATION(mitkSourceImageRelationRule) diff --git a/Modules/Core/test/mitkTemporalJoinImagesFilterTest.cpp b/Modules/Core/test/mitkTemporalJoinImagesFilterTest.cpp new file mode 100644 index 0000000000..4f2035ef7e --- /dev/null +++ b/Modules/Core/test/mitkTemporalJoinImagesFilterTest.cpp @@ -0,0 +1,188 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +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 "mitkImagePixelReadAccessor.h" + +#include "mitkTemporalJoinImagesFilter.h" +#include "mitkTestFixture.h" +#include "mitkTestingMacros.h" + +#include +#include "mitkTestDynamicImageGenerator.h" +#include "mitkStringProperty.h" +#include "mitkTemporoSpatialStringProperty.h" + +class mitkTemporalJoinImagesFilterTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkTemporalJoinImagesFilterTestSuite); + + MITK_TEST(InvalidUsage); + MITK_TEST(FuseDefault); + MITK_TEST(FuseUseDefinedTimeBounds); + + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::Image::Pointer t0; + mitk::Image::Pointer t1; + mitk::Image::Pointer t2; + + mitk::Image::Pointer invalidPixel; + mitk::Image::Pointer invalidGeometry; + +public: + void setUp() override + { + t0 = mitk::GenerateTestFrame(1); + t0->SetProperty("all", mitk::StringProperty::New("all_0")); + t0->SetProperty("ts", mitk::TemporoSpatialStringProperty::New("ts_0")); + + t1 = mitk::GenerateTestFrame(20); + t1->SetProperty("all", mitk::StringProperty::New("all_1")); + t1->SetProperty("ts", mitk::TemporoSpatialStringProperty::New("ts_1")); + t1->SetProperty("special", mitk::StringProperty::New("special_1")); + + t2 = mitk::GenerateTestFrame(300); + t2->SetProperty("all", mitk::StringProperty::New("all_2")); + t2->SetProperty("ts", mitk::TemporoSpatialStringProperty::New("ts_2")); + + invalidPixel = mitk::Image::New(); + invalidPixel->Initialize(mitk::MakePixelType(), *(t0->GetTimeGeometry())); + + invalidGeometry = mitk::Image::New(); + invalidGeometry->Initialize(t0); + invalidGeometry->SetGeometry(mitk::Geometry3D::New()); + } + + void tearDown() override {} + + void InvalidUsage() + { + auto filter = mitk::TemporalJoinImagesFilter::New(); + + filter->SetInput(0, t0); + filter->SetInput(1, invalidPixel); + + CPPUNIT_ASSERT_THROW(filter->Update(), mitk::Exception); + + filter = mitk::TemporalJoinImagesFilter::New(); + + filter->SetInput(0, t0); + filter->SetInput(1, invalidGeometry); + + CPPUNIT_ASSERT_THROW(filter->Update(), mitk::Exception); + + filter = mitk::TemporalJoinImagesFilter::New(); + + filter->SetInput(0, t0); + filter->SetInput(1, t1); + filter->SetMaxTimeBounds({ 3 }); + + CPPUNIT_ASSERT_THROW(filter->Update(), mitk::Exception); + } + + void FuseDefault() + { + auto filter = mitk::TemporalJoinImagesFilter::New(); + + filter->SetInput(0, t0); + filter->SetInput(1, t1); + filter->SetInput(2, t2); + + filter->Update(); + auto output = filter->GetOutput(); + + CPPUNIT_ASSERT(output->GetTimeSteps() == 3); + auto allProp = output->GetProperty("all"); + CPPUNIT_ASSERT(allProp->GetValueAsString() == "all_0"); + auto specialProp = output->GetProperty("special"); + CPPUNIT_ASSERT(specialProp->GetValueAsString() == "special_1"); + auto tsProp = dynamic_cast(output->GetProperty("ts").GetPointer()); + CPPUNIT_ASSERT(tsProp->GetValueByTimeStep(0) == "ts_0"); + CPPUNIT_ASSERT(tsProp->GetValueByTimeStep(1) == "ts_1"); + CPPUNIT_ASSERT(tsProp->GetValueByTimeStep(2) == "ts_2"); + + CPPUNIT_ASSERT(mitk::Equal(*(t0->GetGeometry()), *(output->GetGeometry()), mitk::eps, true)); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMinimumTimePoint(0) == 0.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMaximumTimePoint(0) == 1.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMinimumTimePoint(1) == 1.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMaximumTimePoint(1) == 2.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMinimumTimePoint(2) == 2.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMaximumTimePoint(2) == 3.); + + mitk::ImagePixelReadAccessor accessor(output); + itk::Index<4> testIndex0; + testIndex0.Fill(2); + testIndex0[3] = 0; + itk::Index<4> testIndex1; + testIndex1.Fill(2); + testIndex1[3] = 1; + itk::Index<4> testIndex2; + testIndex2.Fill(2); + testIndex2[3] = 2; + + CPPUNIT_ASSERT_EQUAL(28., accessor.GetPixelByIndex(testIndex0)); + CPPUNIT_ASSERT_EQUAL(180., accessor.GetPixelByIndex(testIndex1)); + CPPUNIT_ASSERT_EQUAL(2420., accessor.GetPixelByIndex(testIndex2)); + } + + void FuseUseDefinedTimeBounds() + { + auto filter = mitk::TemporalJoinImagesFilter::New(); + + filter->SetInput(0, t0); + filter->SetInput(1, t1); + filter->SetInput(2, t2); + + filter->SetFirstMinTimeBound(10); + filter->SetMaxTimeBounds({ 20,30,40 }); + + filter->Update(); + auto output = filter->GetOutput(); + + CPPUNIT_ASSERT(output->GetTimeSteps() == 3); + auto allProp = output->GetProperty("all"); + CPPUNIT_ASSERT(allProp->GetValueAsString() == "all_0"); + auto specialProp = output->GetProperty("special"); + CPPUNIT_ASSERT(specialProp->GetValueAsString() == "special_1"); + auto tsProp = dynamic_cast(output->GetProperty("ts").GetPointer()); + CPPUNIT_ASSERT(tsProp->GetValueByTimeStep(0) == "ts_0"); + CPPUNIT_ASSERT(tsProp->GetValueByTimeStep(1) == "ts_1"); + CPPUNIT_ASSERT(tsProp->GetValueByTimeStep(2) == "ts_2"); + + CPPUNIT_ASSERT(mitk::Equal(*(t0->GetGeometry()), *(output->GetGeometry()), mitk::eps, true)); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMinimumTimePoint(0) == 10.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMaximumTimePoint(0) == 20.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMinimumTimePoint(1) == 20.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMaximumTimePoint(1) == 30.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMinimumTimePoint(2) == 30.); + CPPUNIT_ASSERT(output->GetTimeGeometry()->GetMaximumTimePoint(2) == 40.); + + mitk::ImagePixelReadAccessor accessor(output); + itk::Index<4> testIndex0; + testIndex0.Fill(2); + testIndex0[3] = 0; + itk::Index<4> testIndex1; + testIndex1.Fill(2); + testIndex1[3] = 1; + itk::Index<4> testIndex2; + testIndex2.Fill(2); + testIndex2[3] = 2; + + CPPUNIT_ASSERT_EQUAL(28., accessor.GetPixelByIndex(testIndex0)); + CPPUNIT_ASSERT_EQUAL(180., accessor.GetPixelByIndex(testIndex1)); + CPPUNIT_ASSERT_EQUAL(2420., accessor.GetPixelByIndex(testIndex2)); + } + +}; + +MITK_TEST_SUITE_REGISTRATION(mitkTemporalJoinImagesFilter) diff --git a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp index e0962e9f77..ae79c6260b 100644 --- a/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp +++ b/Modules/Core/test/mitkTemporoSpatialStringPropertyTest.cpp @@ -1,146 +1,242 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTemporoSpatialStringProperty.h" #include "mitkTestFixture.h" #include "mitkTestingMacros.h" #include class mitkTemporoSpatialStringPropertyTestSuite : public mitk::TestFixture { CPPUNIT_TEST_SUITE(mitkTemporoSpatialStringPropertyTestSuite); MITK_TEST(GetValue); MITK_TEST(HasValue); MITK_TEST(SetValue); + MITK_TEST(IsUniform); MITK_TEST(serializeTemporoSpatialStringPropertyToJSON); MITK_TEST(deserializeJSONToTemporoSpatialStringProperty); CPPUNIT_TEST_SUITE_END(); private: std::string refJSON; + std::string refJSON_legacy; + std::string refPartlyCondensibleJSON; + std::string refCondensibleJSON; mitk::TemporoSpatialStringProperty::Pointer refProp; + mitk::TemporoSpatialStringProperty::Pointer refPartlyCondensibleProp; + mitk::TemporoSpatialStringProperty::Pointer refCondensibleProp; public: void setUp() override { - refJSON = "{\"values\":[{\"t\":0, \"z\":0, \"value\":\"v_0_0\"}, {\"t\":3, \"z\":0, \"value\":\"v_3_0\"}, " - "{\"t\":3, \"z\":2, \"value\":\"v_3_2\"}, {\"t\":6, \"z\":1, \"value\":\"v_6_1\"}]}"; + refJSON_legacy = "{\"values\":[{\"t\":0, \"z\":0, \"value\":\"v_0_0\"}, {\"t\":3, \"z\":0, \"value\":\"v_3_0\"}, " + "{\"t\":3, \"z\":2, \"value\":\"v_3_2\"}, {\"t\":6, \"z\":1, \"value\":\"v_6_1\"}]}"; + refJSON = "{\"values\":[{\"z\":0, \"t\":0, \"value\":\"v_0_0\"}, {\"z\":0, \"t\":3, \"value\":\"v_3_0\"}, {\"z\":1, \"t\":6, \"value\":\"v_6_1\"}, {\"z\":2, \"t\":3, \"value\":\"v_3_2\"}]}"; + refPartlyCondensibleJSON = "{\"values\":[{\"z\":0, \"t\":0, \"value\":\"0\"}, {\"z\":1, \"t\":0, \"tmax\":1, \"value\":\"0\"}, {\"z\":1, \"t\":3, \"tmax\":5, \"value\":\"0\"}, {\"z\":1, \"t\":6, \"value\":\"otherValue\"}, {\"z\":2, \"t\":6, \"value\":\"0\"}]}"; + refCondensibleJSON = "{\"values\":[{\"z\":0, \"zmax\":2, \"t\":0, \"tmax\":1, \"value\":\"1\"}]}"; refProp = mitk::TemporoSpatialStringProperty::New(); refProp->SetValue(0, 0, "v_0_0"); refProp->SetValue(3, 0, "v_3_0"); refProp->SetValue(3, 2, "v_3_2"); refProp->SetValue(6, 1, "v_6_1"); + + refPartlyCondensibleProp = mitk::TemporoSpatialStringProperty::New(); + refPartlyCondensibleProp->SetValue(0, 0, "0"); + refPartlyCondensibleProp->SetValue(0, 1, "0"); + refPartlyCondensibleProp->SetValue(1, 1, "0"); + refPartlyCondensibleProp->SetValue(3, 1, "0"); + refPartlyCondensibleProp->SetValue(4, 1, "0"); + refPartlyCondensibleProp->SetValue(5, 1, "0"); + refPartlyCondensibleProp->SetValue(6, 1, "otherValue"); + refPartlyCondensibleProp->SetValue(6, 2, "0"); + + refCondensibleProp = mitk::TemporoSpatialStringProperty::New(); + refCondensibleProp->SetValue(0, 0, "1"); + refCondensibleProp->SetValue(1, 0, "1"); + refCondensibleProp->SetValue(0, 1, "1"); + refCondensibleProp->SetValue(1, 1, "1"); + refCondensibleProp->SetValue(0, 2, "1"); + refCondensibleProp->SetValue(1, 2, "1"); } void tearDown() override {} + void GetValue() { CPPUNIT_ASSERT(refProp->GetValue() == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 0) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 2) == "v_3_2"); CPPUNIT_ASSERT(refProp->GetValue(3, 1, false, true) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValue(3, 5, false, true) == "v_3_2"); CPPUNIT_ASSERT(refProp->GetValueBySlice(0) == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValueBySlice(4, true) == "v_0_0"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(3) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(6) == "v_6_1"); CPPUNIT_ASSERT(refProp->GetValueByTimeStep(5, true) == "v_3_0"); CPPUNIT_ASSERT(refProp->GetValueAsString() == "v_0_0"); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps().size() == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[1] == 3); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps()[2] == 6); + CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(0).size() == 2); + CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(0)[0] == 0); + CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(0)[1] == 3); + + CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(1).size() == 1); + CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(1)[0] == 6); + + CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps(5).size() == 0); + + CPPUNIT_ASSERT(refProp->GetAvailableSlices().size() == 3); + CPPUNIT_ASSERT(refProp->GetAvailableSlices()[0] == 0); + CPPUNIT_ASSERT(refProp->GetAvailableSlices()[1] == 1); + CPPUNIT_ASSERT(refProp->GetAvailableSlices()[2] == 2); + CPPUNIT_ASSERT(refProp->GetAvailableSlices(0).size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3).size() == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3)[0] == 0); CPPUNIT_ASSERT(refProp->GetAvailableSlices(3)[1] == 2); CPPUNIT_ASSERT(refProp->GetAvailableSlices(2).size() == 0); } void HasValue() { CPPUNIT_ASSERT(refProp->HasValue()); CPPUNIT_ASSERT(refProp->HasValue(3, 0)); CPPUNIT_ASSERT(refProp->HasValue(3, 2)); CPPUNIT_ASSERT(refProp->HasValue(3, 1, false, true)); CPPUNIT_ASSERT(refProp->HasValue(3, 5, false, true)); CPPUNIT_ASSERT(!refProp->HasValue(3, 1)); CPPUNIT_ASSERT(!refProp->HasValue(3, 5)); CPPUNIT_ASSERT(refProp->HasValue(4, 2, true, true)); CPPUNIT_ASSERT(refProp->HasValue(4, 2, true, false)); CPPUNIT_ASSERT(!refProp->HasValue(4, 2, false, true)); CPPUNIT_ASSERT(refProp->HasValueBySlice(0)); CPPUNIT_ASSERT(refProp->HasValueBySlice(4, true)); CPPUNIT_ASSERT(!refProp->HasValueBySlice(4)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(3)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(6)); CPPUNIT_ASSERT(refProp->HasValueByTimeStep(5, true)); CPPUNIT_ASSERT(!refProp->HasValueByTimeStep(5)); } void SetValue() { CPPUNIT_ASSERT_NO_THROW(refProp->SetValue(8, 9, "v_8_9")); CPPUNIT_ASSERT(refProp->GetValue(8, 9) == "v_8_9"); CPPUNIT_ASSERT_NO_THROW(refProp->SetValue("newValue")); CPPUNIT_ASSERT(refProp->GetValue(0, 0) == "newValue"); CPPUNIT_ASSERT(refProp->GetAvailableTimeSteps().size() == 1); CPPUNIT_ASSERT(refProp->GetAvailableSlices(0).size() == 1); } + void IsUniform() + { + CPPUNIT_ASSERT(!refProp->IsUniform()); + CPPUNIT_ASSERT(!refPartlyCondensibleProp->IsUniform()); + CPPUNIT_ASSERT(refCondensibleProp->IsUniform()); + } + void serializeTemporoSpatialStringPropertyToJSON() { std::string data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refProp); CPPUNIT_ASSERT(refJSON == - data); //"Testing serializeTemporoSpatialStringPropertyToJSON() producing correct string."); + data); //"Testing serializeTemporoSpatialStringPropertyToJSON() producing correct string."); + + data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refPartlyCondensibleProp); + CPPUNIT_ASSERT(refPartlyCondensibleJSON == + data); + + data = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(refCondensibleProp); + CPPUNIT_ASSERT(refCondensibleJSON == + data); } void deserializeJSONToTemporoSpatialStringProperty() { mitk::BaseProperty::Pointer prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refJSON); auto *tsProp = dynamic_cast(prop.GetPointer()); CPPUNIT_ASSERT( tsProp->GetValue(0, 0) == "v_0_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 1"); CPPUNIT_ASSERT( tsProp->GetValue(3, 0) == "v_3_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 2"); CPPUNIT_ASSERT( tsProp->GetValue(3, 2) == "v_3_2"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 3"); CPPUNIT_ASSERT( tsProp->GetValue(6, 1) == "v_6_1"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 4"); CPPUNIT_ASSERT(*tsProp == *refProp); //"Testing deserializeJSONToTemporoSpatialStringProperty()"); + + prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refJSON_legacy); + tsProp = dynamic_cast(prop.GetPointer()); + CPPUNIT_ASSERT( + tsProp->GetValue(0, 0) == + "v_0_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 1"); + CPPUNIT_ASSERT( + tsProp->GetValue(3, 0) == + "v_3_0"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 2"); + CPPUNIT_ASSERT( + tsProp->GetValue(3, 2) == + "v_3_2"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 3"); + CPPUNIT_ASSERT( + tsProp->GetValue(6, 1) == + "v_6_1"); //"Testing deserializeJSONToTemporoSpatialStringProperty() producing property with correct value 4"); + CPPUNIT_ASSERT(*tsProp == *refProp); //"Testing deserializeJSONToTemporoSpatialStringProperty()"); + + + prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refPartlyCondensibleJSON); + tsProp = dynamic_cast(prop.GetPointer()); + CPPUNIT_ASSERT(tsProp->GetValue(0, 0) =="0"); + CPPUNIT_ASSERT(tsProp->GetValue(0, 1) == "0"); + CPPUNIT_ASSERT(tsProp->GetValue(1, 1) == "0"); + CPPUNIT_ASSERT(tsProp->GetValue(3, 1) == "0"); + CPPUNIT_ASSERT(tsProp->GetValue(4, 1) == "0"); + CPPUNIT_ASSERT(tsProp->GetValue(5, 1) == "0"); + CPPUNIT_ASSERT(tsProp->GetValue(6, 1) == "otherValue"); + CPPUNIT_ASSERT(tsProp->GetValue(6, 2) == "0"); + CPPUNIT_ASSERT(*tsProp == *refPartlyCondensibleProp); + + prop = mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty(refCondensibleJSON); + tsProp = dynamic_cast(prop.GetPointer()); + CPPUNIT_ASSERT(tsProp->GetValue(0, 0) == "1"); + CPPUNIT_ASSERT(tsProp->GetValue(1, 0) == "1"); + CPPUNIT_ASSERT(tsProp->GetValue(0, 1) == "1"); + CPPUNIT_ASSERT(tsProp->GetValue(1, 1) == "1"); + CPPUNIT_ASSERT(tsProp->GetValue(0, 2) == "1"); + CPPUNIT_ASSERT(tsProp->GetValue(1, 2) == "1"); + CPPUNIT_ASSERT(*tsProp == *refCondensibleProp); } }; MITK_TEST_SUITE_REGISTRATION(mitkTemporoSpatialStringProperty) diff --git a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.cpp b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.cpp index 643fd7dc94..b03d29e22e 100644 --- a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.cpp +++ b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.cpp @@ -1,217 +1,217 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 __mitkDICOMPMIO__cpp #define __mitkDICOMPMIO__cpp #include "mitkDICOMPMIO.h" #include "mitkDICOMPMIOMimeTypes.h" #include #include #include #include #include #include #include #include #include #include "mitkParamapPresetsParser.h" // us #include #include // model fit parameters #include "mitkModelFitConstants.h" namespace mitk { DICOMPMIO::DICOMPMIO() : AbstractFileIO(Image::GetStaticNameOfClass(), mitk::MitkDICOMPMIOMimeTypes::DICOMPM_MIMETYPE_NAME(), "DICOM PM") { AbstractFileWriter::SetRanking(10); AbstractFileReader::SetRanking(10); this->RegisterService(); } IFileIO::ConfidenceLevel DICOMPMIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; const Image *PMinput = static_cast(this->GetInput()); if (PMinput) { auto modalityProperty = PMinput->GetProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x0060).c_str()); if (modalityProperty.IsNotNull()) { std::string modality = modalityProperty->GetValueAsString(); if (modality == "PM") { return Supported; } else return Unsupported; } else return Unsupported; } else return Unsupported; } void DICOMPMIO::Write() { ValidateOutputLocation(); mitk::LocaleSwitch localeSwitch("C"); LocalFile localFile(this); const std::string path = localFile.GetFileName(); auto PMinput = dynamic_cast(this->GetInput()); if (PMinput == nullptr) mitkThrow() << "Cannot write non-image data"; // Get DICOM information from referenced image vector> dcmDatasetsSourceImage; std::unique_ptr readFileFormat(new DcmFileFormat()); try { // Generate dcmdataset witk DICOM tags from property list; ATM the source are the filepaths from the // property list mitk::StringLookupTableProperty::Pointer filesProp = dynamic_cast(PMinput->GetProperty("referenceFiles").GetPointer()); if (filesProp.IsNull()) { mitkThrow() << "No property with dicom file path."; return; } // returns a list of all referenced files 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 information: " << e.what() << endl; return; } mitk::Image *mitkPMImage = const_cast(PMinput); // Cast input PMinput to itk image ImageToItk::Pointer PMimageToItkFilter = ImageToItk::New(); PMimageToItkFilter->SetInput(mitkPMImage); // Cast from original itk type to dcmqi input itk image type typedef itk::CastImageFilter castItkImageFilterType; castItkImageFilterType::Pointer castFilter = castItkImageFilterType::New(); castFilter->SetInput(PMimageToItkFilter->GetOutput()); castFilter->Update(); PMitkInternalImageType::Pointer itkParamapImage = castFilter->GetOutput(); // Create PM meta information const std::string tmpMetaInfoFile = this->CreateMetaDataJsonFilePM(); // Convert itk PM images to dicom image MITK_INFO << "Writing PM image: " << path << std::endl; try { // convert from unique to raw pointer vector rawVecDataset; for ( const auto& dcmDataSet : dcmDatasetsSourceImage ) { rawVecDataset.push_back( dcmDataSet.get() ); } std::unique_ptr PMconverter(new dcmqi::ParaMapConverter()); std::unique_ptr PMresult (PMconverter->itkimage2paramap(itkParamapImage, rawVecDataset, tmpMetaInfoFile)); // Write dicom file DcmFileFormat dcmFileFormat(PMresult.get()); std::string filePath = path.substr(0, path.find_last_of(".")); filePath = filePath + ".dcm"; dcmFileFormat.saveFile(filePath.c_str(), EXS_LittleEndianExplicit); } catch (const std::exception &e) { MITK_ERROR << "An error occurred during writing the DICOM Paramap: " << e.what() << endl; return; } } const std::string mitk::DICOMPMIO::CreateMetaDataJsonFilePM() const { const mitk::Image *PMimage = dynamic_cast(this->GetInput()); dcmqi::JSONParametricMapMetaInformationHandler PMhandler; // Get Metadata from modelFitConstants std::string parameterName; PMimage->GetPropertyList()->GetStringProperty(ModelFitConstants::PARAMETER_NAME_PROPERTY_NAME().c_str(), parameterName); std::string modelName; PMimage->GetPropertyList()->GetStringProperty(ModelFitConstants::MODEL_NAME_PROPERTY_NAME().c_str(), modelName); mitk::ParamapPresetsParser* pmPresets = mitk::ParamapPresetsParser::New(); // Here the mitkParamapPresets.xml file containing the Coding Schmeme Designator and Code Value are parsed and the relevant values extracted pmPresets->LoadPreset(); auto pmType_parameterName = pmPresets->GetType(parameterName); auto pmType_modelName = pmPresets->GetType(modelName); // Pass codes to Paramap Converter PMhandler.setDerivedPixelContrast("TCS"); PMhandler.setFrameLaterality("U"); PMhandler.setQuantityValueCode(pmType_parameterName.codeValue, pmType_parameterName.codeScheme, parameterName); PMhandler.setMeasurementMethodCode(pmType_modelName.codeValue, pmType_modelName.codeScheme, modelName); PMhandler.setMeasurementUnitsCode("/min", "UCUM", "/m"); PMhandler.setSeriesNumber("1"); PMhandler.setInstanceNumber("1"); PMhandler.setDerivationCode("129104", "DCM", "Perfusion image analysis"); PMhandler.setRealWorldValueSlope("1"); return PMhandler.getJSONOutputAsString(); } - std::vector DICOMPMIO::Read() + std::vector DICOMPMIO::DoRead() { mitk::LocaleSwitch localeSwitch("C"); std::vector result; return result; } IFileIO::ConfidenceLevel DICOMPMIO::GetReaderConfidenceLevel() const { return Unsupported; } DICOMPMIO *DICOMPMIO::IOClone() const { return new DICOMPMIO(*this); } } // namespace #endif //__mitkDICOMPMIO__cpp diff --git a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h index f7ecd5cb52..46056c0d3e 100644 --- a/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.h +++ b/Modules/DICOMPM/autoload/DICOMPMIO/mitkDICOMPMIO.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 __mitkDICOMPMIO_h #define __mitkDICOMPMIO_h #include #include #include #include #include namespace mitk { /** * Read and Writes a Parametric map to a dcm file * @ingroup Process */ class DICOMPMIO : public mitk::AbstractFileIO { public: DICOMPMIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - /** - * @brief Reads a DICOM parametric maps from the file system - * @return a mitk::Image - * @throws throws an mitk::Exception if an error ocurrs - */ - std::vector Read() override; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; + protected: + /** + * @brief Reads a DICOM parametric map from the file system + * @return an mitk::Image + * @throws an mitk::Exception if an error ocurrs + */ + std::vector> DoRead() override; private: typedef mitk::Image PMInputType; typedef itk::Image PMitkInputImageType; typedef IODFloatingPointImagePixelModule::value_type PMFloatPixelType; // input type required for DCMQI typedef itk::Image PMitkInternalImageType; DICOMPMIO *IOClone() const override; // -------------- DICOMPMIO specific functions ------------- const std::string CreateMetaDataJsonFilePM() const; }; } // end of namespace mitk #endif // __mitkDICOMPMIO_h diff --git a/Modules/DICOMReader/files.cmake b/Modules/DICOMReader/files.cmake index e72f2b58c1..7218fa0662 100644 --- a/Modules/DICOMReader/files.cmake +++ b/Modules/DICOMReader/files.cmake @@ -1,62 +1,63 @@ file(GLOB_RECURSE H_FILES RELATIVE "${CMAKE_CURRENT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/include/*") set(CPP_FILES mitkBaseDICOMReaderService.cpp mitkDICOMFileReader.cpp mitkDICOMTagScanner.cpp mitkDICOMGDCMTagScanner.cpp mitkDICOMDCMTKTagScanner.cpp mitkDICOMImageBlockDescriptor.cpp mitkDICOMITKSeriesGDCMReader.cpp mitkDICOMDatasetSorter.cpp mitkDICOMTagBasedSorter.cpp mitkDICOMGDCMImageFrameInfo.cpp mitkDICOMImageFrameInfo.cpp mitkDICOMIOHelper.cpp mitkDICOMGenericImageFrameInfo.cpp mitkDICOMDatasetAccessingImageFrameInfo.cpp mitkDICOMSortCriterion.cpp mitkDICOMSortByTag.cpp mitkITKDICOMSeriesReaderHelper.cpp mitkEquiDistantBlocksSorter.cpp mitkNormalDirectionConsistencySorter.cpp mitkSortByImagePositionPatient.cpp mitkGantryTiltInformation.cpp mitkClassicDICOMSeriesReader.cpp mitkThreeDnTDICOMSeriesReader.cpp mitkDICOMTag.cpp mitkDICOMTagsOfInterestHelper.cpp mitkDICOMTagCache.cpp mitkDICOMGDCMTagCache.cpp mitkDICOMGenericTagCache.cpp mitkDICOMEnums.cpp mitkDICOMReaderConfigurator.cpp mitkDICOMFileReaderSelector.cpp mitkIDICOMTagsOfInterest.cpp mitkDICOMTagPath.cpp mitkDICOMProperty.cpp mitkDICOMFilesHelper.cpp + mitkDICOMIOMetaInformationPropertyConstants.cpp legacy/mitkDicomSeriesReader.cpp legacy/mitkDicomSR_GantryTiltInformation.cpp legacy/mitkDicomSR_ImageBlockDescriptor.cpp legacy/mitkDicomSR_LoadDICOMRGBPixel.cpp legacy/mitkDicomSR_LoadDICOMRGBPixel4D.cpp legacy/mitkDicomSR_LoadDICOMScalar.cpp legacy/mitkDicomSR_LoadDICOMScalar4D.cpp legacy/mitkDicomSR_SliceGroupingResult.cpp ) set(RESOURCE_FILES configurations/3D/classicreader.xml configurations/3D/imageposition.xml configurations/3D/imageposition_byacquisition.xml configurations/3D/instancenumber.xml configurations/3D/instancenumber_soft.xml configurations/3D/slicelocation.xml configurations/3D/simpleinstancenumber_soft.xml configurations/3DnT/classicreader.xml configurations/3DnT/imageposition.xml configurations/3DnT/imageposition_byacquisition.xml configurations/3DnT/imageposition_bytriggertime.xml ) diff --git a/Modules/DICOMReader/include/mitkBaseDICOMReaderService.h b/Modules/DICOMReader/include/mitkBaseDICOMReaderService.h index 1acff14ce9..aa56718ad8 100644 --- a/Modules/DICOMReader/include/mitkBaseDICOMReaderService.h +++ b/Modules/DICOMReader/include/mitkBaseDICOMReaderService.h @@ -1,70 +1,70 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKBASEDICOMREADERSERVICE_H #define MITKBASEDICOMREADERSERVICE_H #include #include #include "MitkDICOMReaderExports.h" namespace mitk { /** Base class for service wrappers that make DICOMFileReader from the DICOMReader module usable. */ class MITKDICOMREADER_EXPORT BaseDICOMReaderService : public AbstractFileReader { public: BaseDICOMReaderService(const std::string& description); BaseDICOMReaderService(const mitk::CustomMimeType& customType, const std::string& description); using AbstractFileReader::Read; - /** Uses this->GetRelevantFile() and this->GetReader to load the image. - * data and puts it into base data instances-*/ - std::vector > Read() override; - IFileReader::ConfidenceLevel GetConfidenceLevel() const override; protected: - /** Returns the list of all DCM files that are in the same directory + /** Uses this->GetRelevantFile() and this->GetReader to load the image. + * data and puts it into base data instances-*/ + std::vector> DoRead() override; + + /** Returns the list of all DCM files that are in the same directory * like this->GetLocalFileName().*/ mitk::StringList GetDICOMFilesInSameDirectory() const; /** Returns the reader instance that should be used. The descission may be based * one the passed relevant file list.*/ virtual mitk::DICOMFileReader::Pointer GetReader(const mitk::StringList& relevantFiles) const = 0; void SetOnlyRegardOwnSeries(bool); bool GetOnlyRegardOwnSeries() const; private: /** Flags that constrols if the read() operation should only regard DICOM files of the same series if the specified GetLocalFileName() is a file. If it is a director, this flag has no impact (it is assumed false then). */ bool m_OnlyRegardOwnSeries = true; }; class IPropertyProvider; /** Helper function that generates a name string (e.g. for DataNode names) from the DICOM properties of the passed provider instance. If the instance is nullptr, or has no dicom properties DataNode::NO_NAME_VALUE() will be returned.*/ std::string MITKDICOMREADER_EXPORT GenerateNameFromDICOMProperties(const mitk::IPropertyProvider* provider); } #endif // MITKBASEDICOMREADERSERVICE_H diff --git a/Modules/DICOMReader/include/mitkDICOMIOMetaInformationPropertyConstants.h b/Modules/DICOMReader/include/mitkDICOMIOMetaInformationPropertyConstants.h new file mode 100644 index 0000000000..5a6bc3eaa7 --- /dev/null +++ b/Modules/DICOMReader/include/mitkDICOMIOMetaInformationPropertyConstants.h @@ -0,0 +1,51 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#ifndef MITKDICOMIOMETAINFORMATIONCONSTANTS_H_ +#define MITKDICOMIOMETAINFORMATIONCONSTANTS_H_ + +#include + +#include "mitkPropertyKeyPath.h" + +namespace mitk +{ + /** + * @ingroup IO + * @brief The IOMetaInformationPropertyConsants struct + */ + struct MITKDICOMREADER_EXPORT DICOMIOMetaInformationPropertyConstants + { + //Path to the property containing the name of the dicom reader configuration used to read the data + static PropertyKeyPath READER_CONFIGURATION(); + //Path to the property containing the files the dicom reader used in a TemporoSpatialProperty + static PropertyKeyPath READER_FILES(); + //Path to the property containing PixelSpacingInterpretationString for the read data + static PropertyKeyPath READER_PIXEL_SPACING_INTERPRETATION_STRING(); + //Path to the property containing PixelSpacingInterpretation for the read data + static PropertyKeyPath READER_PIXEL_SPACING_INTERPRETATION(); + //Path to the property containing ReaderImplementationLevelString for the read data + static PropertyKeyPath READER_IMPLEMENTATION_LEVEL_STRING(); + //Path to the property containing ReaderImplementationLevel for the read data + static PropertyKeyPath READER_IMPLEMENTATION_LEVEL(); + //Path to the property containing the indicator of the gantry tilt was corrected when reading the data + static PropertyKeyPath READER_GANTRY_TILT_CORRECTED(); + //Path to the property containing the indicator of the data was read as 3D+t + static PropertyKeyPath READER_3D_plus_t(); + //Path to the property containing the version of GDCM used to read the data + static PropertyKeyPath READER_GDCM(); + //Path to the property containing the version of DCMTK used to read the data + static PropertyKeyPath READER_DCMTK(); + }; +} + +#endif // MITKIOCONSTANTS_H_ diff --git a/Modules/DICOMReader/src/mitkBaseDICOMReaderService.cpp b/Modules/DICOMReader/src/mitkBaseDICOMReaderService.cpp index aa9d0f3e4b..2163a64b75 100644 --- a/Modules/DICOMReader/src/mitkBaseDICOMReaderService.cpp +++ b/Modules/DICOMReader/src/mitkBaseDICOMReaderService.cpp @@ -1,221 +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 "mitkBaseDICOMReaderService.h" #include #include #include #include #include #include #include #include "legacy/mitkDicomSeriesReader.h" #include #include #include "mitkIPropertyProvider.h" #include "mitkPropertyNameHelper.h" +#include "mitkPropertyKeyPath.h" +#include "mitkDICOMIOMetaInformationPropertyConstants.h" #include #include #include namespace mitk { BaseDICOMReaderService::BaseDICOMReaderService(const std::string& description) : AbstractFileReader(CustomMimeType(IOMimeTypes::DICOM_MIMETYPE()), description) { } BaseDICOMReaderService::BaseDICOMReaderService(const mitk::CustomMimeType& customType, const std::string& description) : AbstractFileReader(customType, description) { } void BaseDICOMReaderService::SetOnlyRegardOwnSeries(bool regard) { m_OnlyRegardOwnSeries = regard; } bool BaseDICOMReaderService::GetOnlyRegardOwnSeries() const { return m_OnlyRegardOwnSeries; } -std::vector > BaseDICOMReaderService::Read() +std::vector > BaseDICOMReaderService::DoRead() { std::vector result; const std::string fileName = this->GetLocalFileName(); //special handling of Philips 3D US DICOM. //Copied from DICOMSeriesReaderService if (DicomSeriesReader::IsPhilips3DDicom(fileName)) { MITK_INFO << "it is a Philips3D US Dicom file" << std::endl; mitk::LocaleSwitch localeSwitch("C"); std::locale previousCppLocale(std::cin.getloc()); std::locale l("C"); std::cin.imbue(l); DataNode::Pointer node = DataNode::New(); mitk::DicomSeriesReader::StringContainer stringvec; stringvec.push_back(fileName); if (DicomSeriesReader::LoadDicomSeries(stringvec, *node)) { BaseData::Pointer data = node->GetData(); StringProperty::Pointer nameProp = StringProperty::New(itksys::SystemTools::GetFilenameName(fileName)); data->GetPropertyList()->SetProperty("name", nameProp); result.push_back(data); } std::cin.imbue(previousCppLocale); return result; } //Normal DICOM handling (It wasn't a Philips 3D US) mitk::StringList relevantFiles = this->GetDICOMFilesInSameDirectory(); if (relevantFiles.empty()) { MITK_INFO << "DICOMReader service found no relevant files in specified location. No data is loaded. Location: "<GetReader(relevantFiles); if(reader.IsNull()) { MITK_INFO << "DICOMReader service found no suitable reader configuration for relevant files."; } else { if (!pathIsDirectory) { //we ensure that we only load the relevant image block files const auto nrOfOutputs = reader->GetNumberOfOutputs(); for (unsigned int outputIndex = 0; outputIndex < nrOfOutputs; ++outputIndex) { const auto frameList = reader->GetOutput(outputIndex).GetImageFrameList(); auto finding = std::find_if(frameList.begin(), frameList.end(), [&](const DICOMImageFrameInfo::Pointer& frame) { return frame->Filename == fileName; }); if (finding != frameList.end()) { //we have the block containing the fileName -> these are the realy relevant files. relevantFiles.resize(frameList.size()); std::transform(frameList.begin(), frameList.end(), relevantFiles.begin(), [](const DICOMImageFrameInfo::Pointer& frame) { return frame->Filename; }); break; } } } const unsigned int ntotalfiles = relevantFiles.size(); for( unsigned int i=0; i< ntotalfiles; i++) { m_ReadFiles.push_back( relevantFiles.at(i) ); } reader->SetAdditionalTagsOfInterest(mitk::GetCurrentDICOMTagsOfInterest()); reader->SetTagLookupTableToPropertyFunctor(mitk::GetDICOMPropertyForDICOMValuesFunctor); reader->SetInputFiles(relevantFiles); mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); scanner->AddTagPaths(reader->GetTagsOfInterest()); scanner->SetInputFiles(relevantFiles); scanner->Scan(); reader->SetTagCache(scanner->GetScanCache()); reader->AnalyzeInputFiles(); reader->LoadImages(); for (unsigned int i = 0; i < reader->GetNumberOfOutputs(); ++i) { const mitk::DICOMImageBlockDescriptor& desc = reader->GetOutput(i); mitk::BaseData::Pointer data = desc.GetMitkImage().GetPointer(); std::string nodeName = GenerateNameFromDICOMProperties(&desc); StringProperty::Pointer nameProp = StringProperty::New(nodeName); data->SetProperty("name", nameProp); + data->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_CONFIGURATION()), StringProperty::New(reader->GetConfigurationLabel())); + result.push_back(data); } } } return result; } StringList BaseDICOMReaderService::GetDICOMFilesInSameDirectory() const { std::string fileName = this->GetLocalFileName(); mitk::StringList relevantFiles = mitk::GetDICOMFilesInSameDirectory(fileName); return relevantFiles; } IFileReader::ConfidenceLevel BaseDICOMReaderService::GetConfidenceLevel() const { IFileReader::ConfidenceLevel abstractConfidence = AbstractFileReader::GetConfidenceLevel(); if (Unsupported == abstractConfidence) { if (itksys::SystemTools::FileIsDirectory(this->GetInputLocation().c_str())) { // In principle we support dicom directories return Supported; } } return abstractConfidence; } std::string GenerateNameFromDICOMProperties(const mitk::IPropertyProvider* provider) { std::string nodeName = mitk::DataNode::NO_NAME_VALUE(); auto studyProp = provider->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x1030).c_str()); if (studyProp.IsNotNull()) { nodeName = studyProp->GetValueAsString(); } auto seriesProp = provider->GetConstProperty(mitk::GeneratePropertyNameForDICOMTag(0x0008, 0x103E).c_str()); if (seriesProp.IsNotNull()) { if (studyProp.IsNotNull()) { nodeName += " / "; } else { nodeName = ""; } nodeName += seriesProp->GetValueAsString(); } return nodeName; }; } diff --git a/Modules/DICOMReader/src/mitkDICOMFileReader.cpp b/Modules/DICOMReader/src/mitkDICOMFileReader.cpp index 8e774692e7..1c497e6908 100644 --- a/Modules/DICOMReader/src/mitkDICOMFileReader.cpp +++ b/Modules/DICOMReader/src/mitkDICOMFileReader.cpp @@ -1,225 +1,227 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkDICOMFileReader.h" #include mitk::DICOMFileReader ::DICOMFileReader() :itk::Object() { } mitk::DICOMFileReader ::~DICOMFileReader() { } mitk::DICOMFileReader ::DICOMFileReader(const DICOMFileReader& other ) :itk::Object() ,m_InputFilenames( other.m_InputFilenames ) ,m_Outputs( other.m_Outputs ) ,m_ConfigLabel( other.m_ConfigLabel ) ,m_ConfigDescription( other.m_ConfigDescription ) { } mitk::DICOMFileReader& mitk::DICOMFileReader ::operator=(const DICOMFileReader& other) { if (this != &other) { m_InputFilenames = other.m_InputFilenames; m_Outputs = other.m_Outputs; m_ConfigLabel = other.m_ConfigLabel; m_ConfigDescription = other.m_ConfigDescription; + m_AdditionalTagsOfInterest = other.m_AdditionalTagsOfInterest; + m_TagLookupTableToPropertyFunctor = other.m_TagLookupTableToPropertyFunctor; } return *this; } void mitk::DICOMFileReader ::SetConfigurationLabel(const std::string& label) { m_ConfigLabel = label; this->Modified(); } std::string mitk::DICOMFileReader ::GetConfigurationLabel() const { return m_ConfigLabel; } void mitk::DICOMFileReader ::SetConfigurationDescription(const std::string& desc) { m_ConfigDescription = desc; this->Modified(); } std::string mitk::DICOMFileReader ::GetConfigurationDescription() const { return m_ConfigDescription; } void mitk::DICOMFileReader ::SetInputFiles( const StringList& filenames) { m_InputFilenames = filenames; this->Modified(); } const mitk::StringList& mitk::DICOMFileReader ::GetInputFiles() const { return m_InputFilenames; } unsigned int mitk::DICOMFileReader ::GetNumberOfOutputs() const { return m_Outputs.size(); } void mitk::DICOMFileReader ::ClearOutputs() { m_Outputs.clear(); } void mitk::DICOMFileReader ::SetNumberOfOutputs(unsigned int numberOfOutputs) { m_Outputs.resize(numberOfOutputs); } void mitk::DICOMFileReader ::SetOutput(unsigned int index, const mitk::DICOMImageBlockDescriptor& output) { if (index < m_Outputs.size()) { m_Outputs[index] = output; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_Outputs.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } void mitk::DICOMFileReader ::PrintConfiguration(std::ostream& os) const { os << "---- Configuration of " << this->GetNameOfClass() <<" " << (void*)this << " ----"<< std::endl; this->InternalPrintConfiguration(os); os << "---- End of configuration ----" << std::endl; } void mitk::DICOMFileReader ::PrintOutputs(std::ostream& os, bool filenameDetails) const { os << "---- Outputs of DICOMFilereader " << (void*)this << " ----"<< std::endl; for (unsigned int o = 0; o < m_Outputs.size(); ++o) { os << "-- Output " << o << std::endl; const DICOMImageBlockDescriptor& block = m_Outputs[o]; block.Print(os, filenameDetails); } os << "---- End of output list ----" << std::endl; } const mitk::DICOMImageBlockDescriptor& mitk::DICOMFileReader ::GetOutput(unsigned int index) const { if (index < m_Outputs.size()) { return m_Outputs[index]; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_Outputs.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } mitk::DICOMImageBlockDescriptor& mitk::DICOMFileReader ::InternalGetOutput(unsigned int index) { if (index < m_Outputs.size()) { return m_Outputs[index]; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_Outputs.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMFileReader ::IsDICOM(const std::string& filename) { itk::GDCMImageIO::Pointer io = itk::GDCMImageIO::New(); return io->CanReadFile( filename.c_str() ); } mitk::DICOMFileReader::AdditionalTagsMapType mitk::DICOMFileReader::GetAdditionalTagsOfInterest() const { return m_AdditionalTagsOfInterest; } void mitk::DICOMFileReader::SetAdditionalTagsOfInterest( const AdditionalTagsMapType& tagList) { m_AdditionalTagsOfInterest = tagList; this->Modified(); } void mitk::DICOMFileReader::SetTagLookupTableToPropertyFunctor( mitk::DICOMImageBlockDescriptor::TagLookupTableToPropertyFunctor functor ) { m_TagLookupTableToPropertyFunctor = functor; this->Modified(); } mitk::DICOMImageBlockDescriptor::TagLookupTableToPropertyFunctor mitk::DICOMFileReader::GetTagLookupTableToPropertyFunctor() const { return m_TagLookupTableToPropertyFunctor; } diff --git a/Modules/DICOMReader/src/mitkDICOMIOMetaInformationPropertyConstants.cpp b/Modules/DICOMReader/src/mitkDICOMIOMetaInformationPropertyConstants.cpp new file mode 100644 index 0000000000..beecb531ed --- /dev/null +++ b/Modules/DICOMReader/src/mitkDICOMIOMetaInformationPropertyConstants.cpp @@ -0,0 +1,67 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +#include "mitkDICOMIOMetaInformationPropertyConstants.h" + +namespace mitk +{ + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_FILES() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "files" }); + } + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION_STRING() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "PixelSpacingInterpretationString" }); + } + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "PixelSpacingInterpretation" }); + } + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL_STRING() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "ReaderImplementationLevelString" }); + } + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "ReaderImplementationLevel" }); + } + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_GANTRY_TILT_CORRECTED() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "GantyTiltCorrected" }); + } + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_3D_plus_t() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "3D+t" }); + } + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_GDCM() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "gdcm" }); + } + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_DCMTK() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "dcmtk" }); + } + + PropertyKeyPath DICOMIOMetaInformationPropertyConstants::READER_CONFIGURATION() + { + return PropertyKeyPath({ "MITK", "IO", "reader", "DICOM", "configuration" }); + } +} diff --git a/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp b/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp index b4244d44d2..69ca0c9109 100644 --- a/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp +++ b/Modules/DICOMReader/src/mitkDICOMImageBlockDescriptor.cpp @@ -1,888 +1,913 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkDICOMImageBlockDescriptor.h" #include "mitkStringProperty.h" #include "mitkLevelWindowProperty.h" +#include "mitkPropertyKeyPath.h" +#include "mitkDICOMIOMetaInformationPropertyConstants.h" #include #include +#include +#include mitk::DICOMImageBlockDescriptor::DICOMImageBlockDescriptor() : m_ReaderImplementationLevel( SOPClassUnknown ) , m_PropertyList( PropertyList::New() ) , m_TagCache( nullptr ) , m_PropertiesOutOfDate( true ) { m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues; } mitk::DICOMImageBlockDescriptor::~DICOMImageBlockDescriptor() { } mitk::DICOMImageBlockDescriptor::DICOMImageBlockDescriptor( const DICOMImageBlockDescriptor& other ) : m_ImageFrameList( other.m_ImageFrameList ) , m_MitkImage( other.m_MitkImage ) , m_SliceIsLoaded( other.m_SliceIsLoaded ) , m_ReaderImplementationLevel( other.m_ReaderImplementationLevel ) , m_TiltInformation( other.m_TiltInformation ) , m_PropertyList( other.m_PropertyList->Clone() ) , m_TagCache( other.m_TagCache ) , m_PropertiesOutOfDate( other.m_PropertiesOutOfDate ) , m_AdditionalTagMap(other.m_AdditionalTagMap) , m_FoundAdditionalTags(other.m_FoundAdditionalTags) , m_PropertyFunctor(other.m_PropertyFunctor) { if ( m_MitkImage ) { m_MitkImage = m_MitkImage->Clone(); } - - m_PropertyFunctor = &mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues; } mitk::DICOMImageBlockDescriptor& mitk::DICOMImageBlockDescriptor:: operator=( const DICOMImageBlockDescriptor& other ) { if ( this != &other ) { m_ImageFrameList = other.m_ImageFrameList; m_MitkImage = other.m_MitkImage; m_SliceIsLoaded = other.m_SliceIsLoaded; m_ReaderImplementationLevel = other.m_ReaderImplementationLevel; m_TiltInformation = other.m_TiltInformation; m_AdditionalTagMap = other.m_AdditionalTagMap; m_FoundAdditionalTags = other.m_FoundAdditionalTags; m_PropertyFunctor = other.m_PropertyFunctor; if ( other.m_PropertyList ) { m_PropertyList = other.m_PropertyList->Clone(); } if ( other.m_MitkImage ) { m_MitkImage = other.m_MitkImage->Clone(); } m_TagCache = other.m_TagCache; m_PropertiesOutOfDate = other.m_PropertiesOutOfDate; } return *this; } mitk::DICOMTagList mitk::DICOMImageBlockDescriptor::GetTagsOfInterest() { DICOMTagList completeList; completeList.push_back( DICOMTag( 0x0018, 0x1164 ) ); // pixel spacing completeList.push_back( DICOMTag( 0x0028, 0x0030 ) ); // imager pixel spacing completeList.push_back( DICOMTag( 0x0008, 0x0018 ) ); // sop instance UID completeList.push_back( DICOMTag( 0x0008, 0x0016 ) ); // sop class UID completeList.push_back( DICOMTag( 0x0020, 0x0011 ) ); // series number completeList.push_back( DICOMTag( 0x0008, 0x1030 ) ); // study description completeList.push_back( DICOMTag( 0x0008, 0x103e ) ); // series description completeList.push_back( DICOMTag( 0x0008, 0x0060 ) ); // modality completeList.push_back( DICOMTag( 0x0018, 0x0024 ) ); // sequence name completeList.push_back( DICOMTag( 0x0020, 0x0037 ) ); // image orientation completeList.push_back( DICOMTag( 0x0020, 0x1041 ) ); // slice location completeList.push_back( DICOMTag( 0x0020, 0x0012 ) ); // acquisition number completeList.push_back( DICOMTag( 0x0020, 0x0013 ) ); // instance number completeList.push_back( DICOMTag( 0x0020, 0x0032 ) ); // image position patient completeList.push_back( DICOMTag( 0x0028, 0x1050 ) ); // window center completeList.push_back( DICOMTag( 0x0028, 0x1051 ) ); // window width completeList.push_back( DICOMTag( 0x0008, 0x0008 ) ); // image type completeList.push_back( DICOMTag( 0x0028, 0x0004 ) ); // photometric interpretation return completeList; } void mitk::DICOMImageBlockDescriptor::SetAdditionalTagsOfInterest( const AdditionalTagsMapType& tagMap) { m_AdditionalTagMap = tagMap; } void mitk::DICOMImageBlockDescriptor::SetTiltInformation( const GantryTiltInformation& info ) { m_TiltInformation = info; } const mitk::GantryTiltInformation mitk::DICOMImageBlockDescriptor::GetTiltInformation() const { return m_TiltInformation; } void mitk::DICOMImageBlockDescriptor::SetImageFrameList( const DICOMImageFrameList& framelist ) { m_ImageFrameList = framelist; m_SliceIsLoaded.resize( framelist.size() ); m_SliceIsLoaded.assign( framelist.size(), false ); m_PropertiesOutOfDate = true; } const mitk::DICOMImageFrameList& mitk::DICOMImageBlockDescriptor::GetImageFrameList() const { return m_ImageFrameList; } void mitk::DICOMImageBlockDescriptor::SetMitkImage( Image::Pointer image ) { if ( m_MitkImage != image ) { if ( m_TagCache.IsExpired() ) { MITK_ERROR << "Unable to describe MITK image with properties without a tag-cache object!"; m_MitkImage = nullptr; return; } if ( m_ImageFrameList.empty() ) { MITK_ERROR << "Unable to describe MITK image with properties without a frame list!"; m_MitkImage = nullptr; return; } // Should verify that the image matches m_ImageFrameList and m_TagCache // however, this is hard to do without re-analyzing all // TODO we should at least make sure that the number of frames is identical (plus rows/columns, // orientation) // without gantry tilt correction, we can also check image origin m_MitkImage = this->DescribeImageWithProperties( this->FixupSpacing( image ) ); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::GetMitkImage() const { return m_MitkImage; } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::FixupSpacing( Image* mitkImage ) { if ( mitkImage ) { Vector3D imageSpacing = mitkImage->GetGeometry()->GetSpacing(); ScalarType desiredSpacingX = imageSpacing[0]; ScalarType desiredSpacingY = imageSpacing[1]; this->GetDesiredMITKImagePixelSpacing( desiredSpacingX, desiredSpacingY ); // prefer pixel spacing over imager pixel spacing if ( desiredSpacingX <= 0 || desiredSpacingY <= 0 ) { return mitkImage; } MITK_DEBUG << "Loaded image with spacing " << imageSpacing[0] << ", " << imageSpacing[1]; MITK_DEBUG << "Found correct spacing info " << desiredSpacingX << ", " << desiredSpacingY; imageSpacing[0] = desiredSpacingX; imageSpacing[1] = desiredSpacingY; mitkImage->GetGeometry()->SetSpacing( imageSpacing ); } return mitkImage; } void mitk::DICOMImageBlockDescriptor::SetSliceIsLoaded( unsigned int index, bool isLoaded ) { if ( index < m_SliceIsLoaded.size() ) { m_SliceIsLoaded[index] = isLoaded; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor::IsSliceLoaded( unsigned int index ) const { if ( index < m_SliceIsLoaded.size() ) { return m_SliceIsLoaded[index]; } else { std::stringstream ss; ss << "Index " << index << " out of range (" << m_SliceIsLoaded.size() << " indices reserved)"; throw std::invalid_argument( ss.str() ); } } bool mitk::DICOMImageBlockDescriptor::AllSlicesAreLoaded() const { bool allLoaded = true; for ( auto iter = m_SliceIsLoaded.cbegin(); iter != m_SliceIsLoaded.cend(); ++iter ) { allLoaded &= *iter; } return allLoaded; } /* PS defined IPS defined PS==IPS 0 0 --> UNKNOWN spacing, loader will invent 0 1 --> spacing as at detector surface 1 0 --> spacing as in patient 1 1 0 --> detector surface spacing CORRECTED for geometrical magnifications: spacing as in patient 1 1 1 --> detector surface spacing NOT corrected for geometrical magnifications: spacing as at detector */ mitk::PixelSpacingInterpretation mitk::DICOMImageBlockDescriptor::GetPixelSpacingInterpretation() const { if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() ) { MITK_ERROR << "Invalid call to GetPixelSpacingInterpretation. Need to have initialized tag-cache!"; return SpacingUnknown; } const std::string pixelSpacing = this->GetPixelSpacing(); const std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); if ( pixelSpacing.empty() ) { if ( imagerPixelSpacing.empty() ) { return SpacingUnknown; } else { return SpacingAtDetector; } } else // Pixel Spacing defined { if ( imagerPixelSpacing.empty() ) { return SpacingInPatient; } else if ( pixelSpacing != imagerPixelSpacing ) { return SpacingInPatient; } else { return SpacingAtDetector; } } } std::string mitk::DICOMImageBlockDescriptor::GetPixelSpacing() const { if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() ) { MITK_ERROR << "Invalid call to GetPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagPixelSpacing( 0x0028, 0x0030 ); return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagPixelSpacing ).value; } std::string mitk::DICOMImageBlockDescriptor::GetImagerPixelSpacing() const { if ( m_ImageFrameList.empty() || m_TagCache.IsExpired() ) { MITK_ERROR << "Invalid call to GetImagerPixelSpacing. Need to have initialized tag-cache!"; return std::string( "" ); } static const DICOMTag tagImagerPixelSpacing( 0x0018, 0x1164 ); return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagImagerPixelSpacing ).value; } void mitk::DICOMImageBlockDescriptor::GetDesiredMITKImagePixelSpacing( ScalarType& spacingX, ScalarType& spacingY ) const { const std::string pixelSpacing = this->GetPixelSpacing(); // preference for "in patient" pixel spacing if ( !DICOMStringToSpacing( pixelSpacing, spacingX, spacingY ) ) { const std::string imagerPixelSpacing = this->GetImagerPixelSpacing(); // fallback to "on detector" spacing if ( !DICOMStringToSpacing( imagerPixelSpacing, spacingX, spacingY ) ) { // at this point we have no hints whether the spacing is correct // do a quick sanity check and either trust in the input or set both to 1 // We assume neither spacing to be negative, zero or unexpectedly large for // medical images if (spacingX < mitk::eps || spacingX > 1000 || spacingY < mitk::eps || spacingY > 1000) { spacingX = spacingY = 1.0; } } } } void mitk::DICOMImageBlockDescriptor::SetProperty( const std::string& key, BaseProperty* value ) { m_PropertyList->SetProperty( key, value ); } mitk::BaseProperty* mitk::DICOMImageBlockDescriptor::GetProperty( const std::string& key ) const { this->UpdateImageDescribingProperties(); return m_PropertyList->GetProperty( key ); } std::string mitk::DICOMImageBlockDescriptor::GetPropertyAsString( const std::string& key ) const { this->UpdateImageDescribingProperties(); const mitk::BaseProperty::Pointer property = m_PropertyList->GetProperty( key ); if ( property.IsNotNull() ) { return property->GetValueAsString(); } else { return std::string( "" ); } } void mitk::DICOMImageBlockDescriptor::SetFlag( const std::string& key, bool value ) { m_PropertyList->ReplaceProperty( key, BoolProperty::New( value ) ); } bool mitk::DICOMImageBlockDescriptor::GetFlag( const std::string& key, bool defaultValue ) const { this->UpdateImageDescribingProperties(); BoolProperty::ConstPointer boolProp = dynamic_cast( this->GetProperty( key ) ); if ( boolProp.IsNotNull() ) { return boolProp->GetValue(); } else { return defaultValue; } } void mitk::DICOMImageBlockDescriptor::SetIntProperty( const std::string& key, int value ) { m_PropertyList->ReplaceProperty( key, IntProperty::New( value ) ); } int mitk::DICOMImageBlockDescriptor::GetIntProperty( const std::string& key, int defaultValue ) const { this->UpdateImageDescribingProperties(); IntProperty::ConstPointer intProp = dynamic_cast( this->GetProperty( key ) ); if ( intProp.IsNotNull() ) { return intProp->GetValue(); } else { return defaultValue; } } double mitk::DICOMImageBlockDescriptor::stringtodouble( const std::string& str ) const { double d; std::string trimmedstring( str ); try { trimmedstring = trimmedstring.erase( trimmedstring.find_last_not_of( " \n\r\t" ) + 1 ); } catch ( ... ) { // no last not of } std::string firstcomponent( trimmedstring ); try { firstcomponent = trimmedstring.erase( trimmedstring.find_first_of( "\\" ) ); } catch ( ... ) { // no last not of } std::istringstream converter( firstcomponent ); if ( !firstcomponent.empty() && ( converter >> d ) && converter.eof() ) { return d; } else { throw std::invalid_argument( "Argument is not a convertable number" ); } } mitk::Image::Pointer mitk::DICOMImageBlockDescriptor::DescribeImageWithProperties( Image* mitkImage ) { // TODO: this is a collection of properties that have been provided by the // legacy DicomSeriesReader. // We should at some point clean up this collection and name them in a more // consistent way! if ( !mitkImage ) return mitkImage; + mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_FILES()), this->GetProperty("filenamesForSlices")); + mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION_STRING()), + StringProperty::New(PixelSpacingInterpretationToString(this->GetPixelSpacingInterpretation()))); + mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION()), + GenericProperty::New(this->GetPixelSpacingInterpretation())); + mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL_STRING()), + StringProperty::New(ReaderImplementationLevelToString(m_ReaderImplementationLevel))); + mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL()), + GenericProperty::New(m_ReaderImplementationLevel)); + mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_GANTRY_TILT_CORRECTED()), + BoolProperty::New(this->GetTiltInformation().IsRegularGantryTilt())); + mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_3D_plus_t()), BoolProperty::New(this->GetFlag("3D+t", false))); + mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_GDCM()), StringProperty::New(gdcm::Version::GetVersion())); + mitkImage->SetProperty(PropertyKeyPathToPropertyName(DICOMIOMetaInformationPropertyConstants::READER_DCMTK()), StringProperty::New(PACKAGE_VERSION)); + + // get all found additional tags of interest + + for (auto tag : m_FoundAdditionalTags) + { + BaseProperty* prop = this->GetProperty(tag); + if (prop) + { + mitkImage->SetProperty(tag.c_str(), prop); + } + } + + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + //// Deprecated properties should be removed sooner then later (see above) + ///////////////////////////////////////////////////////////////////////////// + ///////////////////////////////////////////////////////////////////////////// + // first part: add some tags that describe individual slices // these propeties are defined at analysis time (see UpdateImageDescribingProperties()) const char* propertyKeySliceLocation = "dicom.image.0020.1041"; const char* propertyKeyInstanceNumber = "dicom.image.0020.0013"; const char* propertyKeySOPInstanceUID = "dicom.image.0008.0018"; mitkImage->SetProperty( propertyKeySliceLocation, this->GetProperty( "sliceLocationForSlices" ) ); mitkImage->SetProperty( propertyKeyInstanceNumber, this->GetProperty( "instanceNumberForSlices" ) ); mitkImage->SetProperty( propertyKeySOPInstanceUID, this->GetProperty( "SOPInstanceUIDForSlices" ) ); - mitkImage->SetProperty( "files", this->GetProperty( "filenamesForSlices" ) ); + mitkImage->SetProperty( "files", this->GetProperty( "filenamesForSlices_deprecated" ) ); // second part: add properties that describe the whole image block mitkImage->SetProperty( "dicomseriesreader.SOPClassUID", StringProperty::New( this->GetSOPClassUID() ) ); mitkImage->SetProperty( "dicomseriesreader.SOPClass", StringProperty::New( this->GetSOPClassUIDAsName() ) ); mitkImage->SetProperty( "dicomseriesreader.PixelSpacingInterpretationString", StringProperty::New( PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() ) ) ); mitkImage->SetProperty( "dicomseriesreader.PixelSpacingInterpretation", GenericProperty::New( this->GetPixelSpacingInterpretation() ) ); mitkImage->SetProperty( "dicomseriesreader.ReaderImplementationLevelString", StringProperty::New( ReaderImplementationLevelToString( m_ReaderImplementationLevel ) ) ); mitkImage->SetProperty( "dicomseriesreader.ReaderImplementationLevel", GenericProperty::New( m_ReaderImplementationLevel ) ); mitkImage->SetProperty( "dicomseriesreader.GantyTiltCorrected", BoolProperty::New( this->GetTiltInformation().IsRegularGantryTilt() ) ); mitkImage->SetProperty( "dicomseriesreader.3D+t", BoolProperty::New( this->GetFlag( "3D+t", false ) ) ); // level window const std::string windowCenter = this->GetPropertyAsString( "windowCenter" ); const std::string windowWidth = this->GetPropertyAsString( "windowWidth" ); try { const double level = stringtodouble( windowCenter ); const double window = stringtodouble( windowWidth ); mitkImage->SetProperty( "levelwindow", LevelWindowProperty::New( LevelWindow( level, window ) ) ); } catch ( ... ) { // nothing, no levelwindow to be predicted... } const std::string modality = this->GetPropertyAsString( "modality" ); mitkImage->SetProperty( "modality", StringProperty::New( modality ) ); mitkImage->SetProperty( "dicom.pixel.PhotometricInterpretation", this->GetProperty( "photometricInterpretation" ) ); mitkImage->SetProperty( "dicom.image.imagetype", this->GetProperty( "imagetype" ) ); mitkImage->SetProperty( "dicom.study.StudyDescription", this->GetProperty( "studyDescription" ) ); mitkImage->SetProperty( "dicom.series.SeriesDescription", this->GetProperty( "seriesDescription" ) ); mitkImage->SetProperty( "dicom.pixel.Rows", this->GetProperty( "rows" ) ); mitkImage->SetProperty( "dicom.pixel.Columns", this->GetProperty( "columns" ) ); - // third part: get all found additional tags of interest - - for (auto tag : m_FoundAdditionalTags) - { - BaseProperty* prop = this->GetProperty(tag); - if (prop) - { - mitkImage->SetProperty(tag.c_str(), prop); - } - } - // fourth part: get something from ImageIO. BUT this needs to be created elsewhere. or not at all! return mitkImage; } void mitk::DICOMImageBlockDescriptor::SetReaderImplementationLevel( const ReaderImplementationLevel& level ) { m_ReaderImplementationLevel = level; } mitk::ReaderImplementationLevel mitk::DICOMImageBlockDescriptor::GetReaderImplementationLevel() const { return m_ReaderImplementationLevel; } std::string mitk::DICOMImageBlockDescriptor::GetSOPClassUID() const { if ( !m_ImageFrameList.empty() && !m_TagCache.IsExpired() ) { static const DICOMTag tagSOPClassUID( 0x0008, 0x0016 ); return m_TagCache.Lock()->GetTagValue( m_ImageFrameList.front(), tagSOPClassUID ).value; } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUID(). Need to have initialized tag-cache!"; return std::string( "" ); } } std::string mitk::DICOMImageBlockDescriptor::GetSOPClassUIDAsName() const { if ( !m_ImageFrameList.empty() && !m_TagCache.IsExpired() ) { gdcm::UIDs uidKnowledge; uidKnowledge.SetFromUID( this->GetSOPClassUID().c_str() ); const char* name = uidKnowledge.GetName(); if ( name ) { return std::string( name ); } else { return std::string( "" ); } } else { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::GetSOPClassUIDAsName(). Need to have " "initialized tag-cache!"; return std::string( "" ); } } int mitk::DICOMImageBlockDescriptor::GetNumberOfTimeSteps() const { int result = 1; this->m_PropertyList->GetIntProperty("timesteps", result); return result; }; int mitk::DICOMImageBlockDescriptor::GetNumberOfFramesPerTimeStep() const { const int numberOfTimesteps = this->GetNumberOfTimeSteps(); int numberOfFramesPerTimestep = this->m_ImageFrameList.size() / numberOfTimesteps; assert(int(double((double)this->m_ImageFrameList.size() / (double)numberOfTimesteps)) == numberOfFramesPerTimestep); // this should hold return numberOfFramesPerTimestep; }; void mitk::DICOMImageBlockDescriptor::SetTagCache( DICOMTagCache* privateCache ) { // this must only be used during loading and never afterwards m_TagCache = privateCache; } #define printPropertyRange( label, property_name ) \ \ { \ const std::string first = this->GetPropertyAsString( #property_name "First" ); \ const std::string last = this->GetPropertyAsString( #property_name "Last" ); \ if ( !first.empty() || !last.empty() ) \ { \ if ( first == last ) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ else \ { \ os << " " label ": '" << first << "' - '" << last << "'" << std::endl; \ } \ } \ \ } #define printProperty( label, property_name ) \ \ { \ const std::string first = this->GetPropertyAsString( #property_name ); \ if ( !first.empty() ) \ { \ os << " " label ": '" << first << "'" << std::endl; \ } \ \ } #define printBool( label, commands ) \ \ { \ os << " " label ": '" << ( commands ? "yes" : "no" ) << "'" << std::endl; \ \ } void mitk::DICOMImageBlockDescriptor::Print(std::ostream& os, bool filenameDetails) const { os << " Number of Frames: '" << m_ImageFrameList.size() << "'" << std::endl; os << " SOP class: '" << this->GetSOPClassUIDAsName() << "'" << std::endl; printProperty( "Series Number", seriesNumber ); printProperty( "Study Description", studyDescription ); printProperty( "Series Description", seriesDescription ); printProperty( "Modality", modality ); printProperty( "Sequence Name", sequenceName ); printPropertyRange( "Slice Location", sliceLocation ); printPropertyRange( "Acquisition Number", acquisitionNumber ); printPropertyRange( "Instance Number", instanceNumber ); printPropertyRange( "Image Position", imagePositionPatient ); printProperty( "Image Orientation", orientation ); os << " Pixel spacing interpretation: '" << PixelSpacingInterpretationToString( this->GetPixelSpacingInterpretation() ) << "'" << std::endl; printBool( "Gantry Tilt", this->GetTiltInformation().IsRegularGantryTilt() ) // printBool("3D+t", this->GetFlag("3D+t",false)) // os << " MITK image loaded: '" << (this->GetMitkImage().IsNotNull() ? "yes" : "no") << "'" << // std::endl; if ( filenameDetails ) { os << " Files in this image block:" << std::endl; for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++frameIter ) { os << " " << ( *frameIter )->Filename; if ( ( *frameIter )->FrameNo > 0 ) { os << ", " << ( *frameIter )->FrameNo; } os << std::endl; } } } #define storeTagValueToProperty( tag_name, tag_g, tag_e ) \ \ { \ const DICOMTag t( tag_g, tag_e ); \ const std::string tagValue = tagCache->GetTagValue( firstFrame, t ).value; \ const_cast( this ) \ ->SetProperty( #tag_name, StringProperty::New( tagValue ) ); \ \ } #define storeTagValueRangeToProperty( tag_name, tag_g, tag_e ) \ \ { \ const DICOMTag t( tag_g, tag_e ); \ const std::string tagValueFirst = tagCache->GetTagValue( firstFrame, t ).value; \ const std::string tagValueLast = tagCache->GetTagValue( lastFrame, t ).value; \ const_cast( this ) \ ->SetProperty( #tag_name "First", StringProperty::New( tagValueFirst ) ); \ const_cast( this ) \ ->SetProperty( #tag_name "Last", StringProperty::New( tagValueLast ) ); \ \ } void mitk::DICOMImageBlockDescriptor::UpdateImageDescribingProperties() const { if ( !m_PropertiesOutOfDate ) return; if ( !m_ImageFrameList.empty() ) { if ( m_TagCache.IsExpired() ) { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::UpdateImageDescribingProperties(). Need to " "have initialized tag-cache!"; return; } auto tagCache = m_TagCache.Lock(); if (tagCache.IsNull()) { MITK_ERROR << "Invalid call to DICOMImageBlockDescriptor::UpdateImageDescribingProperties(). Need to " "have initialized tag-cache!"; return; } const DICOMImageFrameInfo::Pointer firstFrame = m_ImageFrameList.front(); const DICOMImageFrameInfo::Pointer lastFrame = m_ImageFrameList.back(); // see macros above storeTagValueToProperty( seriesNumber, 0x0020, 0x0011 ); storeTagValueToProperty( studyDescription, 0x0008, 0x1030 ); storeTagValueToProperty( seriesDescription, 0x0008, 0x103e ); storeTagValueToProperty( modality, 0x0008, 0x0060 ); storeTagValueToProperty( sequenceName, 0x0018, 0x0024 ); storeTagValueToProperty( orientation, 0x0020, 0x0037 ); storeTagValueToProperty( rows, 0x0028, 0x0010 ); storeTagValueToProperty( columns, 0x0028, 0x0011 ); storeTagValueRangeToProperty( sliceLocation, 0x0020, 0x1041 ); storeTagValueRangeToProperty( acquisitionNumber, 0x0020, 0x0012 ); storeTagValueRangeToProperty( instanceNumber, 0x0020, 0x0013 ); storeTagValueRangeToProperty( imagePositionPatient, 0x0020, 0x0032 ); storeTagValueToProperty( windowCenter, 0x0028, 0x1050 ); storeTagValueToProperty( windowWidth, 0x0028, 0x1051 ); storeTagValueToProperty( imageType, 0x0008, 0x0008 ); storeTagValueToProperty( photometricInterpretation, 0x0028, 0x0004 ); // some per-image attributes // frames are just numbered starting from 0. timestep 1 (the second time-step) has frames starting at // (number-of-frames-per-timestep) // std::string propertyKeySliceLocation = "dicom.image.0020.1041"; // std::string propertyKeyInstanceNumber = "dicom.image.0020.0013"; // std::string propertyKeySOPInstanceNumber = "dicom.image.0008.0018"; StringLookupTable sliceLocationForSlices; StringLookupTable instanceNumberForSlices; StringLookupTable SOPInstanceUIDForSlices; - StringLookupTable filenamesForSlices; + StringLookupTable filenamesForSlices_deprecated; + DICOMCachedValueLookupTable filenamesForSlices; const DICOMTag tagSliceLocation( 0x0020, 0x1041 ); const DICOMTag tagInstanceNumber( 0x0020, 0x0013 ); const DICOMTag tagSOPInstanceNumber( 0x0008, 0x0018 ); std::unordered_map additionalTagResultList; unsigned int slice(0); int timePoint(-1); const int framesPerTimeStep = this->GetNumberOfFramesPerTimeStep(); for ( auto frameIter = m_ImageFrameList.begin(); frameIter != m_ImageFrameList.end(); ++slice, ++frameIter ) { unsigned int zSlice = slice%framesPerTimeStep; if ( zSlice == 0) { timePoint++; } const std::string sliceLocation = tagCache->GetTagValue( *frameIter, tagSliceLocation ).value; sliceLocationForSlices.SetTableValue( slice, sliceLocation ); const std::string instanceNumber = tagCache->GetTagValue( *frameIter, tagInstanceNumber ).value; instanceNumberForSlices.SetTableValue( slice, instanceNumber ); const std::string sopInstanceUID = tagCache->GetTagValue( *frameIter, tagSOPInstanceNumber ).value; SOPInstanceUIDForSlices.SetTableValue( slice, sopInstanceUID ); const std::string filename = ( *frameIter )->Filename; - filenamesForSlices.SetTableValue( slice, filename ); + filenamesForSlices_deprecated.SetTableValue( slice, filename ); + filenamesForSlices.SetTableValue(slice, { static_cast(timePoint), zSlice, filename }); MITK_DEBUG << "Tag info for slice " << slice << ": SL '" << sliceLocation << "' IN '" << instanceNumber << "' SOP instance UID '" << sopInstanceUID << "'"; for (const auto& tag : m_AdditionalTagMap) { const DICOMTagCache::FindingsListType findings = tagCache->GetTagValue( *frameIter, tag.first ); for (const auto& finding : findings) { if (finding.isValid) { std::string propKey = (tag.second.empty()) ? DICOMTagPathToPropertyName(finding.path) : tag.second; DICOMCachedValueInfo info{ static_cast(timePoint), zSlice, finding.value }; additionalTagResultList[propKey].SetTableValue(slice, info); } } } } // add property or properties with proper names auto* thisInstance = const_cast( this ); thisInstance->SetProperty( "sliceLocationForSlices", StringLookupTableProperty::New( sliceLocationForSlices ) ); thisInstance->SetProperty( "instanceNumberForSlices", StringLookupTableProperty::New( instanceNumberForSlices ) ); thisInstance->SetProperty( "SOPInstanceUIDForSlices", StringLookupTableProperty::New( SOPInstanceUIDForSlices ) ); - thisInstance->SetProperty( "filenamesForSlices", StringLookupTableProperty::New( filenamesForSlices ) ); - + thisInstance->SetProperty( "filenamesForSlices_deprecated", StringLookupTableProperty::New( filenamesForSlices_deprecated ) ); + thisInstance->SetProperty("filenamesForSlices", m_PropertyFunctor(filenamesForSlices)); //add properties for additional tags of interest for ( auto iter = additionalTagResultList.cbegin(); iter != additionalTagResultList.cend(); ++iter ) { thisInstance->SetProperty( iter->first, m_PropertyFunctor( iter->second ) ); thisInstance->m_FoundAdditionalTags.insert(m_FoundAdditionalTags.cend(),iter->first); } m_PropertiesOutOfDate = false; } } mitk::BaseProperty::Pointer mitk::DICOMImageBlockDescriptor::GetPropertyForDICOMValues(const DICOMCachedValueLookupTable& cacheLookupTable) { const auto& lookupTable = cacheLookupTable.GetLookupTable(); typedef std::pair PairType; if ( std::adjacent_find( lookupTable.cbegin(), lookupTable.cend(), []( const PairType& lhs, const PairType& rhs ) { return lhs.second.Value != rhs.second.Value; } ) == lookupTable.cend() ) { return static_cast( mitk::StringProperty::New(cacheLookupTable.GetTableValue(0).Value).GetPointer()); } StringLookupTable stringTable; for (auto element : lookupTable) { stringTable.SetTableValue(element.first, element.second.Value); } return static_cast( mitk::StringLookupTableProperty::New(stringTable).GetPointer()); } void mitk::DICOMImageBlockDescriptor::SetTagLookupTableToPropertyFunctor( TagLookupTableToPropertyFunctor functor ) { if ( functor != nullptr ) { m_PropertyFunctor = functor; } } mitk::BaseProperty::ConstPointer mitk::DICOMImageBlockDescriptor::GetConstProperty(const std::string &propertyKey, const std::string &/*contextName*/, bool /*fallBackOnDefaultContext*/) const { this->UpdateImageDescribingProperties(); return m_PropertyList->GetConstProperty(propertyKey); }; std::vector mitk::DICOMImageBlockDescriptor::GetPropertyKeys(const std::string &/*contextName*/, bool /*includeDefaultContext*/) const { this->UpdateImageDescribingProperties(); return m_PropertyList->GetPropertyKeys(); }; std::vector mitk::DICOMImageBlockDescriptor::GetPropertyContextNames() const { return std::vector(); }; diff --git a/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.cpp b/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.cpp index 6da162eeb6..8896d2b9a9 100644 --- a/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.cpp +++ b/Modules/DICOMReaderServices/src/mitkDICOMReaderServicesActivator.cpp @@ -1,45 +1,93 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkDICOMReaderServicesActivator.h" #include "mitkAutoSelectingDICOMReaderService.h" #include "mitkClassicDICOMSeriesReaderService.h" #include "mitkDICOMTagsOfInterestService.h" #include "mitkSimpleVolumeDICOMSeriesReaderService.h" +#include "mitkCoreServices.h" +#include "mitkPropertyPersistenceInfo.h" +#include "mitkDICOMIOMetaInformationPropertyConstants.h" +#include "mitkIPropertyPersistence.h" +#include "mitkTemporoSpatialStringProperty.h" #include +void AddPropertyPersistence(const mitk::PropertyKeyPath& propPath, bool temporoSpatial = false) +{ + mitk::CoreServicePointer persistenceService(mitk::CoreServices::GetPropertyPersistence()); + + mitk::PropertyPersistenceInfo::Pointer info = mitk::PropertyPersistenceInfo::New(); + if (propPath.IsExplicit()) + { + std::string name = mitk::PropertyKeyPathToPropertyName(propPath); + std::string key = name; + std::replace(key.begin(), key.end(), '.', '_'); + info->SetNameAndKey(name, key); + } + else + { + std::string key = mitk::PropertyKeyPathToPersistenceKeyRegEx(propPath); + std::string keyTemplate = mitk::PropertyKeyPathToPersistenceKeyTemplate(propPath); + std::string propRegEx = mitk::PropertyKeyPathToPropertyRegEx(propPath); + std::string propTemplate = mitk::PropertyKeyPathToPersistenceNameTemplate(propPath); + info->UseRegEx(propRegEx, propTemplate, key, keyTemplate); + } + + if (temporoSpatial) + { + info->SetDeserializationFunction(mitk::PropertyPersistenceDeserialization::deserializeJSONToTemporoSpatialStringProperty); + info->SetSerializationFunction(mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON); + } + + persistenceService->AddInfo(info); +} + namespace mitk { void DICOMReaderServicesActivator::Load(us::ModuleContext* context) { m_AutoSelectingDICOMReader.reset(new AutoSelectingDICOMReaderService()); m_SimpleVolumeDICOMSeriesReader.reset(new SimpleVolumeDICOMSeriesReaderService()); m_DICOMTagsOfInterestService.reset(new DICOMTagsOfInterestService()); context->RegisterService(m_DICOMTagsOfInterestService.get()); DICOMTagPathMapType tagmap = GetDefaultDICOMTagsOfInterest(); for (auto tag : tagmap) { m_DICOMTagsOfInterestService->AddTagOfInterest(tag.first); } + + //add properties that should be persistent (if possible/supported by the writer) + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_3D_plus_t()); + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_CONFIGURATION()); + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_DCMTK()); + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_FILES(), true); + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_GANTRY_TILT_CORRECTED()); + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_GDCM()); + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL()); + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL_STRING()); + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION()); + AddPropertyPersistence(mitk::DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION_STRING()); + } void DICOMReaderServicesActivator::Unload(us::ModuleContext*) { } } US_EXPORT_MODULE_ACTIVATOR(mitk::DICOMReaderServicesActivator) diff --git a/Modules/DICOMTesting/CMakeLists.txt b/Modules/DICOMTesting/CMakeLists.txt index 69d2fcb943..256daf6b3b 100644 --- a/Modules/DICOMTesting/CMakeLists.txt +++ b/Modules/DICOMTesting/CMakeLists.txt @@ -1,41 +1,43 @@ if(BUILD_TESTING) if(GDCM_DIR) # clear variables from prior files.cmake # Else CMake would use the content of these variables and would try to create tests (which are not present in DICOMTesting). set(MODULE_TESTS) set(MODULE_IMAGE_TESTS) set(MODULE_SURFACE_TESTS) set(MODULE_TESTIMAGE) set(MODULE_TESTSURFACE) set(MODULE_CUSTOM_TESTS) set(H_FILES) set(CPP_FILES) # now create a new module only for testing purposes MITK_CREATE_MODULE( DEPENDS MitkDICOMReader + PACKAGE_DEPENDS + PRIVATE GDCM DCMTK ITK|ITKIOGDCM ) mitk_check_module_dependencies(MODULES MitkDICOMTesting MISSING_DEPENDENCIES_VAR _missing_deps) if(_missing_deps) message(STATUS "mitkDICOMTesting module helper applications won't be built. Missing: ${_missing_deps}") else(_missing_deps) # dumps out image information add_executable(DumpDICOMMitkImage src/DumpDICOMMitkImage.cpp) mitk_use_modules(TARGET DumpDICOMMitkImage MODULES MitkDICOMTesting) # compares dumped out image information against reference dump add_executable(VerifyDICOMMitkImageDump src/VerifyDICOMMitkImageDump.cpp) mitk_use_modules(TARGET VerifyDICOMMitkImageDump MODULES MitkDICOMTesting) set_property(TARGET DumpDICOMMitkImage VerifyDICOMMitkImageDump PROPERTY FOLDER "${MITK_ROOT_FOLDER}/Modules/Tests") add_subdirectory(test) endif() endif() endif() diff --git a/Modules/DICOMTesting/include/mitkTestDICOMLoading.h b/Modules/DICOMTesting/include/mitkTestDICOMLoading.h index f1d8cd2f56..63289abc0b 100644 --- a/Modules/DICOMTesting/include/mitkTestDICOMLoading.h +++ b/Modules/DICOMTesting/include/mitkTestDICOMLoading.h @@ -1,108 +1,111 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkTestDICOMLoading_h #define mitkTestDICOMLoading_h #include "mitkClassicDICOMSeriesReader.h" +#include "mitkPropertyKeyPath.h" #include "MitkDICOMTestingExports.h" namespace mitk { class MITKDICOMTESTING_EXPORT TestDICOMLoading { public: typedef std::list ImageList; TestDICOMLoading(); ImageList LoadFiles( const StringList & files ); Image::Pointer DecorateVerifyCachedImage( const StringList& files, mitk::Image::Pointer cachedImage ); Image::Pointer DecorateVerifyCachedImage( const StringList& files, DICOMTagCache*, mitk::Image::Pointer cachedImage ); /** \brief Dump relevant image information for later comparison. \sa CompareImageInformationDumps */ std::string DumpImageInformation( const Image* image ); /** \brief Compare two image information dumps. \return true, if dumps are sufficiently equal (see parameters) \sa DumpImageInformation */ bool CompareImageInformationDumps( const std::string& reference, const std::string& test ); private: typedef std::map KeyValueMap; ClassicDICOMSeriesReader::Pointer BuildDICOMReader(); void SetDefaultLocale(); void ResetUserLocale(); std::string ComponentTypeToString( int type ); KeyValueMap ParseDump( const std::string& dump ); bool CompareSpacedValueFields( const std::string& reference, const std::string& test, double eps = mitk::eps ); /** Compress whitespace in string \param pString input string \param pFill replacement whitespace (only whitespace in string after reduction) \param pWhitespace characters handled as whitespace */ std::string reduce(const std::string& pString, const std::string& pFill = " ", const std::string& pWhitespace = " \t"); /** Remove leading and trailing whitespace \param pString input string \param pWhitespace characters handled as whitespace */ std::string trim(const std::string& pString, const std::string& pWhitespace = " \t"); template bool StringToNumber(const std::string& s, T& value) { std::stringstream stream(s); stream >> value; return (!stream.fail()) && (std::abs(value) <= std::numeric_limits::max()); } + static void AddPropertyToDump(const mitk::PropertyKeyPath& key, const mitk::Image* image, std::stringstream& result); + const char* m_PreviousCLocale; std::locale m_PreviousCppLocale; }; } #endif diff --git a/Modules/DICOMTesting/src/DumpDICOMMitkImage.cpp b/Modules/DICOMTesting/src/DumpDICOMMitkImage.cpp index 240a316037..681794ef6e 100644 --- a/Modules/DICOMTesting/src/DumpDICOMMitkImage.cpp +++ b/Modules/DICOMTesting/src/DumpDICOMMitkImage.cpp @@ -1,36 +1,50 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTestDICOMLoading.h" #include "mitkImage.h" int main(int argc, char** argv) { mitk::TestDICOMLoading loader; mitk::StringList files; - for (int arg = 1; arg < argc; ++arg) files.push_back( argv[arg] ); + if (argc < 2) + { + std::cerr << "Wrong usage of DumpDICOMMitkImage. Call it like VerifyDICOMMitkImageDump [ [... ]]."; + return 1; + } + + std::string dumpPath = argv[1]; + + for (int arg = 2; arg < argc; ++arg) files.push_back( argv[arg] ); mitk::TestDICOMLoading::ImageList images = loader.LoadFiles(files); + std::ostringstream sstream; // combine individual dumps in a way that VerifyDICOMMitkImageDump is able to separate again. // I.e.: when changing this piece of code, always change VerifyDICOMMitkImageDump, too. unsigned int imageCounter(0); for ( mitk::TestDICOMLoading::ImageList::const_iterator imageIter = images.begin(); imageIter != images.end(); ++imageIter ) { - std::cout << "-- Image " << ++imageCounter << "\n"; - std::cout << loader.DumpImageInformation( *imageIter ) << "\n"; + sstream << "-- Image " << ++imageCounter << "\n"; + sstream << loader.DumpImageInformation( *imageIter ) << "\n"; } + std::cout << sstream.str(); + std::ofstream out(dumpPath, ios::trunc | ios::out); + out << sstream.str(); + out.close(); + } diff --git a/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp b/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp index 532204de0e..ed2c8e71b0 100644 --- a/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp +++ b/Modules/DICOMTesting/src/mitkTestDICOMLoading.cpp @@ -1,488 +1,572 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ //#define MBILOG_ENABLE_DEBUG #include "mitkTestDICOMLoading.h" +#include "mitkDICOMIOMetaInformationPropertyConstants.h" +#include "mitkDICOMProperty.h" #include +#include +#include +#include "itksys/SystemTools.hxx" + mitk::TestDICOMLoading::TestDICOMLoading() :m_PreviousCLocale(nullptr) { } void mitk::TestDICOMLoading::SetDefaultLocale() { // remember old locale only once if (m_PreviousCLocale == nullptr) { m_PreviousCLocale = setlocale(LC_NUMERIC, nullptr); // set to "C" setlocale(LC_NUMERIC, "C"); m_PreviousCppLocale = std::cin.getloc(); std::locale l( "C" ); std::cin.imbue(l); std::cout.imbue(l); } } void mitk::TestDICOMLoading::ResetUserLocale() { if (m_PreviousCLocale) { setlocale(LC_NUMERIC, m_PreviousCLocale); std::cin.imbue(m_PreviousCppLocale); std::cout.imbue(m_PreviousCppLocale); m_PreviousCLocale = nullptr; } } mitk::TestDICOMLoading::ImageList mitk::TestDICOMLoading ::LoadFiles( const StringList& files ) { for (auto iter = files.begin(); iter != files.end(); ++iter) { MITK_DEBUG << "File " << *iter; } ImageList result; ClassicDICOMSeriesReader::Pointer reader = this->BuildDICOMReader(); + reader->SetTagLookupTableToPropertyFunctor(mitk::GetDICOMPropertyForDICOMValuesFunctor); reader->SetInputFiles( files ); reader->AnalyzeInputFiles(); reader->PrintOutputs(std::cout,true); reader->LoadImages(); unsigned int numberOfImages = reader->GetNumberOfOutputs(); for (unsigned imageIndex = 0; imageIndex < numberOfImages; ++imageIndex) { const DICOMImageBlockDescriptor& block = reader->GetOutput(imageIndex); result.push_back( block.GetMitkImage() ); } return result; } mitk::ClassicDICOMSeriesReader::Pointer mitk::TestDICOMLoading ::BuildDICOMReader() { ClassicDICOMSeriesReader::Pointer reader = ClassicDICOMSeriesReader::New(); reader->SetFixTiltByShearing(true); return reader; } mitk::Image::Pointer mitk::TestDICOMLoading ::DecorateVerifyCachedImage( const StringList& files, mitk::DICOMTagCache* tagCache, mitk::Image::Pointer cachedImage ) { DICOMImageBlockDescriptor block; DICOMImageFrameList framelist; for (auto iter = files.begin(); iter != files.end(); ++iter) { framelist.push_back( DICOMImageFrameInfo::New(*iter) ); } block.SetImageFrameList( framelist ); block.SetTagCache( tagCache ); block.SetMitkImage( cachedImage ); // this should/will create a propertylist describing the image slices return block.GetMitkImage(); } mitk::Image::Pointer mitk::TestDICOMLoading ::DecorateVerifyCachedImage( const StringList& files, mitk::Image::Pointer cachedImage ) { ClassicDICOMSeriesReader::Pointer reader = this->BuildDICOMReader(); + reader->SetTagLookupTableToPropertyFunctor(mitk::GetDICOMPropertyForDICOMValuesFunctor); reader->SetInputFiles( files ); reader->AnalyzeInputFiles(); // This just creates a "tag cache and a nice DICOMImageBlockDescriptor. // Both of these could also be produced in a different way. The only // important thing is, that the DICOMImageBlockDescriptor knows a // tag-cache object when PropertyDecorateCachedMitkImageForImageBlockDescriptor // is called. if ( reader->GetNumberOfOutputs() != 1 ) { MITK_ERROR << "Reader produce " << reader->GetNumberOfOutputs() << " images instead of 1 expected.."; return nullptr; } DICOMImageBlockDescriptor block = reader->GetOutput(0); // creates a block copy block.SetMitkImage( cachedImage ); // this should/will create a propertylist describing the image slices return block.GetMitkImage(); } std::string mitk::TestDICOMLoading::ComponentTypeToString(int type) { if (type == itk::ImageIOBase::UCHAR) return "UCHAR"; else if (type == itk::ImageIOBase::CHAR) return "CHAR"; else if (type == itk::ImageIOBase::USHORT) return "USHORT"; else if (type == itk::ImageIOBase::SHORT) return "SHORT"; else if (type == itk::ImageIOBase::UINT) return "UINT"; else if (type == itk::ImageIOBase::INT) return "INT"; else if (type == itk::ImageIOBase::ULONG) return "ULONG"; else if (type == itk::ImageIOBase::LONG) return "LONG"; else if (type == itk::ImageIOBase::FLOAT) return "FLOAT"; else if (type == itk::ImageIOBase::DOUBLE) return "DOUBLE"; else return "UNKNOWN"; } // add a line to stringstream result (see DumpImageInformation #define DumpLine(field, data) DumpILine(0, field, data) // add an indented(!) line to stringstream result (see DumpImageInformation #define DumpILine(indent, field, data) \ { \ std::string DumpLine_INDENT; DumpLine_INDENT.resize(indent, ' ' ); \ result << DumpLine_INDENT << field << ": " << data << "\n"; \ } std::string mitk::TestDICOMLoading::DumpImageInformation( const Image* image ) { std::stringstream result; if (image == nullptr) return result.str(); SetDefaultLocale(); // basic image data DumpLine( "Pixeltype", ComponentTypeToString(image->GetPixelType().GetComponentType()) ); DumpLine( "BitsPerPixel", image->GetPixelType().GetBpe() ); DumpLine( "Dimension", image->GetDimension() ); result << "Dimensions: "; for (unsigned int dim = 0; dim < image->GetDimension(); ++dim) result << image->GetDimension(dim) << " "; result << "\n"; // geometry data result << "Geometry: \n"; const TimeGeometry* timeGeometry = image->GetTimeGeometry(); BaseGeometry* geometry = timeGeometry->GetGeometryForTimeStep(0); if (geometry) { AffineTransform3D* transform = geometry->GetIndexToWorldTransform(); if (transform) { result << " " << "Matrix: "; const AffineTransform3D::MatrixType& matrix = transform->GetMatrix(); for (unsigned int i = 0; i < 3; ++i) for (unsigned int j = 0; j < 3; ++j) result << matrix[i][j] << " "; result << "\n"; result << " " << "Offset: "; const AffineTransform3D::OutputVectorType& offset = transform->GetOffset(); for (unsigned int i = 0; i < 3; ++i) result << offset[i] << " "; result << "\n"; result << " " << "Center: "; const AffineTransform3D::InputPointType& center = transform->GetCenter(); for (unsigned int i = 0; i < 3; ++i) result << center[i] << " "; result << "\n"; result << " " << "Translation: "; const AffineTransform3D::OutputVectorType& translation = transform->GetTranslation(); for (unsigned int i = 0; i < 3; ++i) result << translation[i] << " "; result << "\n"; result << " " << "Scale: "; const double* scale = transform->GetScale(); for (unsigned int i = 0; i < 3; ++i) result << scale[i] << " "; result << "\n"; result << " " << "Origin: "; const Point3D& origin = geometry->GetOrigin(); for (unsigned int i = 0; i < 3; ++i) result << origin[i] << " "; result << "\n"; result << " " << "Spacing: "; const Vector3D& spacing = geometry->GetSpacing(); for (unsigned int i = 0; i < 3; ++i) result << spacing[i] << " "; result << "\n"; result << " " << "TimeBounds: "; const TimeBounds timeBounds = timeGeometry->GetTimeBounds(); for (unsigned int i = 0; i < 2; ++i) result << timeBounds[i] << " "; result << "\n"; } } + // io dicom meta information + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_CONFIGURATION(), image, result); + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_FILES(), image, result); + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_GANTRY_TILT_CORRECTED(), image, result); + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL(), image, result); + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_IMPLEMENTATION_LEVEL_STRING(), image, result); + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION(), image, result); + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_PIXEL_SPACING_INTERPRETATION_STRING(), image, result); + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_3D_plus_t(), image, result); + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_DCMTK(), image, result); + AddPropertyToDump(mitk::DICOMIOMetaInformationPropertyConstants::READER_GDCM(), image, result); + ResetUserLocale(); return result.str(); } +void mitk::TestDICOMLoading::AddPropertyToDump(const mitk::PropertyKeyPath& key, const mitk::Image* image, std::stringstream& result) +{ + auto propKey = mitk::PropertyKeyPathToPropertyName(key); + auto prop = image->GetProperty(propKey.c_str()); + if (prop.IsNotNull()) + { + auto value = prop->GetValueAsString(); + auto dicomProp = dynamic_cast< mitk::DICOMProperty*>(prop.GetPointer()); + if (dicomProp != nullptr) + { + auto strippedProp = dicomProp->Clone(); + if (key == mitk::DICOMIOMetaInformationPropertyConstants::READER_FILES()) + {//strip dicom file information from path to ensure generalized dump files + auto timePoints = strippedProp->GetAvailableTimeSteps(); + for (auto timePoint : timePoints) + { + auto slices = strippedProp->GetAvailableSlices(timePoint); + for (auto slice : slices) + { + auto value = strippedProp->GetValue(timePoint, slice); + value = itksys::SystemTools::GetFilenameName(value); + strippedProp->SetValue(timePoint, slice, value); + } + } + } + value = mitk::PropertyPersistenceSerialization::serializeTemporoSpatialStringPropertyToJSON(strippedProp); + } + result << propKey << ": " << value << "\n"; + } +} + std::string mitk::TestDICOMLoading::trim(const std::string& pString, const std::string& pWhitespace) { const size_t beginStr = pString.find_first_not_of(pWhitespace); if (beginStr == std::string::npos) { // no content return ""; } const size_t endStr = pString.find_last_not_of(pWhitespace); const size_t range = endStr - beginStr + 1; return pString.substr(beginStr, range); } std::string mitk::TestDICOMLoading::reduce(const std::string& pString, const std::string& pFill, const std::string& pWhitespace) { // trim first std::string result(trim(pString, pWhitespace)); // replace sub ranges size_t beginSpace = result.find_first_of(pWhitespace); while (beginSpace != std::string::npos) { const size_t endSpace = result.find_first_not_of(pWhitespace, beginSpace); const size_t range = endSpace - beginSpace; result.replace(beginSpace, range, pFill); const size_t newStart = beginSpace + pFill.length(); beginSpace = result.find_first_of(pWhitespace, newStart); } return result; } bool mitk::TestDICOMLoading::CompareSpacedValueFields( const std::string& reference, const std::string& test, double /*eps*/ ) { bool result(true); // tokenize string, compare each token, if possible by float comparison std::stringstream referenceStream(reduce(reference)); std::stringstream testStream(reduce(test)); std::string refToken; std::string testToken; while ( std::getline( referenceStream, refToken, ' ' ) && std::getline ( testStream, testToken, ' ' ) ) { float refNumber; float testNumber; if ( this->StringToNumber(refToken, refNumber) ) { if ( this->StringToNumber(testToken, testNumber) ) { // print-out compared tokens if DEBUG output allowed MITK_DEBUG << "Reference Token '" << refToken << "'" << " value " << refNumber << ", test Token '" << testToken << "'" << " value " << testNumber; bool old_result = result; result &= ( std::abs(refNumber - testNumber) < 0.0001f /*mitk::eps*/ ); // log the token/number which causes the test to fail if( old_result != result) { MITK_ERROR << std::setprecision(16) << "Reference Token '" << refToken << "'" << " value " << refNumber << ", test Token '" << testToken << "'" << " value " << testNumber; MITK_ERROR << "[FALSE] - difference: " << std::setprecision(16) << std::abs(refNumber - testNumber) << " EPS: " << 0.0001f; //mitk::eps; } } else { MITK_ERROR << refNumber << " cannot be compared to '" << testToken << "'"; } } else { MITK_DEBUG << "Token '" << refToken << "'" << " handled as string"; result &= refToken == testToken; } } if ( std::getline( referenceStream, refToken, ' ' ) ) { MITK_ERROR << "Reference string still had values when test string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } else if ( std::getline( testStream, testToken, ' ' ) ) { MITK_ERROR << "Test string still had values when reference string was already parsed: ref '" << reference << "', test '" << test << "'"; result = false; } return result; } bool mitk::TestDICOMLoading::CompareImageInformationDumps( const std::string& referenceDump, const std::string& testDump ) { KeyValueMap reference = ParseDump(referenceDump); KeyValueMap test = ParseDump(testDump); bool testResult(true); // verify all expected values for (KeyValueMap::const_iterator refIter = reference.begin(); refIter != reference.end(); ++refIter) { const std::string& refKey = refIter->first; const std::string& refValue = refIter->second; if ( test.find(refKey) != test.end() ) { const std::string& testValue = test[refKey]; + if (refKey == mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_DCMTK())) + { //check dcmtk version always against the current version of the system + bool thisTestResult = testValue == std::string(" ") + PACKAGE_VERSION; + testResult &= thisTestResult; - bool thisTestResult = CompareSpacedValueFields( refValue, testValue ); - testResult &= thisTestResult; + MITK_DEBUG << refKey << ": '" << PACKAGE_VERSION << "' == '" << testValue << "' ? " << (thisTestResult ? "YES" : "NO"); + } + else if (refKey == mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_GDCM())) + {//check gdcm version always against the current version of the system + bool thisTestResult = testValue == std::string(" ") + gdcm::Version::GetVersion(); + testResult &= thisTestResult; + + MITK_DEBUG << refKey << ": '" << gdcm::Version::GetVersion() << "' == '" << testValue << "' ? " << (thisTestResult ? "YES" : "NO"); + } + else + { + bool thisTestResult = CompareSpacedValueFields(refValue, testValue); + testResult &= thisTestResult; - MITK_DEBUG << refKey << ": '" << refValue << "' == '" << testValue << "' ? " << (thisTestResult?"YES":"NO"); + MITK_DEBUG << refKey << ": '" << refValue << "' == '" << testValue << "' ? " << (thisTestResult ? "YES" : "NO"); + } } else { MITK_ERROR << "Reference dump contains a key'" << refKey << "' (value '" << refValue << "')." ; MITK_ERROR << "This key is expected to be generated for tests (but was not). Most probably you need to update your test data."; return false; } } // now check test dump does not contain any additional keys for (KeyValueMap::const_iterator testIter = test.begin(); testIter != test.end(); ++testIter) { const std::string& key = testIter->first; const std::string& value = testIter->second; - if ( reference.find(key) == reference.end() ) + if (key == mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_DCMTK())) + {//check dcmtk version always against the current version of the system + bool thisTestResult = value == std::string(" ")+PACKAGE_VERSION; + testResult &= thisTestResult; + + MITK_DEBUG << key << ": '" << PACKAGE_VERSION << "' == '" << value << "' ? " << (thisTestResult ? "YES" : "NO"); + } + else if (key == mitk::PropertyKeyPathToPropertyName(mitk::DICOMIOMetaInformationPropertyConstants::READER_GDCM())) + {//check gdcm version always against the current version of the system + bool thisTestResult = value == std::string(" ") + gdcm::Version::GetVersion(); + testResult &= thisTestResult; + + MITK_DEBUG << key << ": '" << gdcm::Version::GetVersion() << "' == '" << value << "' ? " << (thisTestResult ? "YES" : "NO"); + } + else if ( reference.find(key) == reference.end() ) { MITK_ERROR << "Test dump contains an unexpected key'" << key << "' (value '" << value << "')." ; MITK_ERROR << "This key is not expected. Most probably you need to update your test data."; return false; } } return testResult; } mitk::TestDICOMLoading::KeyValueMap mitk::TestDICOMLoading::ParseDump( const std::string& dump ) { KeyValueMap parsedResult; std::string shredder(dump); std::stack surroundingKeys; std::stack expectedIndents; expectedIndents.push(0); while (true) { std::string::size_type newLinePos = shredder.find( '\n' ); if (newLinePos == std::string::npos || newLinePos == 0) break; std::string line = shredder.substr( 0, newLinePos ); shredder = shredder.erase( 0, newLinePos+1 ); std::string::size_type keyPosition = line.find_first_not_of( ' ' ); std::string::size_type colonPosition = line.find( ':' ); std::string key = line.substr(keyPosition, colonPosition - keyPosition); std::string::size_type firstSpacePosition = key.find_first_of(" "); if (firstSpacePosition != std::string::npos) { key.erase(firstSpacePosition); } if ( keyPosition > expectedIndents.top() ) { // more indent than before expectedIndents.push(keyPosition); } - else if (keyPosition == expectedIndents.top() ) + else { if (!surroundingKeys.empty()) { surroundingKeys.pop(); // last of same length } - } - else - { - // less indent than before - do expectedIndents.pop(); - while (expectedIndents.top() != keyPosition); // unwind until current indent is found + + while (expectedIndents.top() != keyPosition) + { + expectedIndents.pop(); + if (!surroundingKeys.empty()) + { + surroundingKeys.pop(); + } + }; // unwind until current indent is found } if (!surroundingKeys.empty()) { key = surroundingKeys.top() + "." + key; // construct current key name } surroundingKeys.push(key); // this is the new embracing key std::string value = line.substr(colonPosition+1); MITK_DEBUG << " Key: '" << key << "' value '" << value << "'" ; parsedResult[key] = value; // store parsing result } return parsedResult; } diff --git a/Modules/DICOMTesting/test/mitkDICOMPreloadedVolumeTest.cpp b/Modules/DICOMTesting/test/mitkDICOMPreloadedVolumeTest.cpp index 69c68e14a3..19ca2db8e3 100644 --- a/Modules/DICOMTesting/test/mitkDICOMPreloadedVolumeTest.cpp +++ b/Modules/DICOMTesting/test/mitkDICOMPreloadedVolumeTest.cpp @@ -1,112 +1,114 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkTestDICOMLoading.h" #include "mitkTestingMacros.h" #include "mitkDICOMTagCache.h" bool CheckAllPropertiesAreInOtherList(const mitk::PropertyList* list, const mitk::PropertyList* otherList) { MITK_TEST_CONDITION_REQUIRED(list && otherList, "Comparison is passed two non-empty property lists") const mitk::PropertyList::PropertyMap* listM = list->GetMap(); const mitk::PropertyList::PropertyMap* otherListM = otherList->GetMap(); bool equal = true; for ( auto iter = listM->begin(); iter != listM->end(); ++iter ) { std::string key = iter->first; mitk::BaseProperty* property = iter->second; auto otherEntry = otherListM->find( key ); MITK_TEST_CONDITION( otherEntry != otherListM->end(), " Property '" << key << "' is contained in other list" ) mitk::BaseProperty* otherProperty = otherEntry->second; - MITK_TEST_CONDITION( equal &= (*property == *otherProperty), " Property '" << key << "' is equal in both list" ) + auto propEqual = (*property == *otherProperty); + MITK_TEST_CONDITION(propEqual, " Property '" << key << "' is equal in both list"); + equal &= propEqual; } return equal; } bool VerifyPropertyListsEquality(const mitk::PropertyList* testList, const mitk::PropertyList* referenceList) { bool allTestPropsInReference = CheckAllPropertiesAreInOtherList(testList, referenceList); MITK_TEST_CONDITION(allTestPropsInReference, "All test properties found in reference properties") bool allReferencePropsInTest = CheckAllPropertiesAreInOtherList(referenceList, testList); MITK_TEST_CONDITION(allReferencePropsInTest, "All reference properties found in test properties") return allTestPropsInReference && allReferencePropsInTest; } // !!! we expect that this tests get a list of files that load as ONE SINGLE mitk::Image! int mitkDICOMPreloadedVolumeTest(int argc, char** const argv) { MITK_TEST_BEGIN("DICOMPreloadedVolume") mitk::TestDICOMLoading loader; mitk::StringList files; // load files from commandline for (int arg = 1; arg < argc; ++arg) { MITK_TEST_OUTPUT(<< "Test file " << argv[arg]) files.push_back( argv[arg] ); } // verify all files are DICOM for (mitk::StringList::const_iterator fileIter = files.begin(); fileIter != files.end(); ++fileIter) { MITK_TEST_CONDITION_REQUIRED( mitk::DICOMFileReader::IsDICOM(*fileIter) , *fileIter << " is recognized as loadable DICOM object" ) } // load for a first time mitk::TestDICOMLoading::ImageList images = loader.LoadFiles(files); MITK_TEST_OUTPUT(<< "Loaded " << images.size() << " images. Remembering properties of the first one for later comparison.") mitk::Image::Pointer firstImage = images.front(); mitk::PropertyList::Pointer originalProperties = firstImage->GetPropertyList(); // save the original firstImage->SetPropertyList( mitk::PropertyList::New() ); // clear image properties // what about DEFAULT properties? currently ONLY nodes get default properties // load for a second time, this time provide the image volume as a pointer // expectation is that the reader will provide the same properties to this image (without actually loading a new mitk::Image) // !!! we expect that this tests get a list of files that load as ONE SINGLE mitk::Image! MITK_TEST_CONDITION_REQUIRED( images.size() == 1, "Not more than 1 images loaded." ); // otherwise, we would need to determine the correct set of files here MITK_TEST_OUTPUT(<< "Generating properties via reader. Comparing new properties to previously loaded version.") mitk::Image::Pointer reloadedImage = loader.DecorateVerifyCachedImage(files, firstImage); MITK_TEST_CONDITION_REQUIRED(reloadedImage.IsNotNull(), "Reader was able to property-decorate image."); mitk::PropertyList::Pointer regeneratedProperties = reloadedImage->GetPropertyList(); // get the version of the second load attempt bool listsAreEqual = VerifyPropertyListsEquality(regeneratedProperties, originalProperties); MITK_TEST_CONDITION(listsAreEqual, "DICOM file reader generates a valid property list when provided a pre-loaded image"); // test again, this time provide a tag cache. // expectation is, that an empty tag cache will lead to NO image mitk::DICOMTagCache::Pointer tagCache; // empty MITK_TEST_OUTPUT(<< "Generating properties via reader. Comparing new properties to previously loaded version.") firstImage->SetPropertyList( mitk::PropertyList::New() ); // clear image properties reloadedImage = loader.DecorateVerifyCachedImage(files, tagCache, firstImage); MITK_TEST_CONDITION_REQUIRED(reloadedImage.IsNull(), "Reader was able to detect missing tag-cache."); MITK_TEST_END() } diff --git a/Modules/DicomRT/autoload/IO/mitkRTDoseReaderService.cpp b/Modules/DicomRT/autoload/IO/mitkRTDoseReaderService.cpp index a1e45309f7..7406172d13 100644 --- a/Modules/DicomRT/autoload/IO/mitkRTDoseReaderService.cpp +++ b/Modules/DicomRT/autoload/IO/mitkRTDoseReaderService.cpp @@ -1,168 +1,168 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mitk { RTDoseReaderService::RTDoseReaderService() : AbstractFileReader(CustomMimeType(mitk::DicomRTMimeTypes::DICOMRT_DOSE_MIMETYPE_NAME()), mitk::DicomRTMimeTypes::DICOMRT_DOSE_MIMETYPE_DESCRIPTION()) { m_FileReaderServiceReg = RegisterService(); } RTDoseReaderService::RTDoseReaderService(const RTDoseReaderService& other) : mitk::AbstractFileReader(other) { } RTDoseReaderService::~RTDoseReaderService() {} template void RTDoseReaderService::MultiplyGridScaling(itk::Image* image, float gridscale) { typedef itk::Image OutputImageType; typedef itk::Image InputImageType; typedef itk::CastImageFilter CastFilterType; typedef itk::ShiftScaleImageFilter ScaleFilterType; typename CastFilterType::Pointer castFilter = CastFilterType::New(); typename ScaleFilterType::Pointer scaleFilter = ScaleFilterType::New(); castFilter->SetInput(image); scaleFilter->SetInput(castFilter->GetOutput()); scaleFilter->SetScale(gridscale); scaleFilter->Update(); typename OutputImageType::Pointer scaledOutput = scaleFilter->GetOutput(); this->scaledDoseImage = mitk::Image::New(); mitk::CastToMitkImage(scaledOutput, this->scaledDoseImage); } - std::vector > RTDoseReaderService::Read() + std::vector > RTDoseReaderService::DoRead() { std::vector > result; mitk::IDICOMTagsOfInterest* toiSrv = GetDicomTagsOfInterestService(); auto tagsOfInterest = toiSrv->GetTagsOfInterest(); DICOMTagPathList tagsOfInterestList; for (const auto& tag : tagsOfInterest) { tagsOfInterestList.push_back(tag.first); } std::string location = GetInputLocation(); mitk::DICOMFileReaderSelector::Pointer selector = mitk::DICOMFileReaderSelector::New(); selector->LoadBuiltIn3DConfigs(); selector->SetInputFiles({ location }); mitk::DICOMFileReader::Pointer reader = selector->GetFirstReaderWithMinimumNumberOfOutputImages(); reader->SetAdditionalTagsOfInterest(toiSrv->GetTagsOfInterest()); reader->SetInputFiles({ location }); reader->AnalyzeInputFiles(); reader->LoadImages(); if (reader->GetNumberOfOutputs() == 0) { MITK_ERROR << "Could not determine a DICOM reader for this file" << std::endl; return result; } mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); scanner->SetInputFiles({ location }); scanner->AddTagPaths(tagsOfInterestList); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); if (frames.empty()) { MITK_ERROR << "Error reading the RTDOSE file" << std::endl; return result; } const mitk::DICOMImageBlockDescriptor& desc = reader->GetOutput(0); mitk::Image::Pointer originalImage = desc.GetMitkImage(); if (originalImage.IsNull()) { MITK_ERROR << "Error reading the RTDOSE file in mitk::DicomFileReader" << std::endl; return result; } DcmFileFormat fileformat; OFCondition outp = fileformat.loadFile(location.c_str(), EXS_Unknown); if (outp.bad()) { MITK_ERROR << "Error reading the RTDOSE file in DCMTK" << std::endl; return result; } DcmDataset *dataset = fileformat.getDataset(); DRTDoseIOD doseObject; OFCondition DCMTKresult = doseObject.read(*dataset); if (DCMTKresult.bad()) { MITK_ERROR << "Error reading the RTDOSE file in DCMTK" << std::endl; return result; } auto findingsGridScaling = frames.front()->GetTagValueAsString(DICOMTagPath(0x3004, 0x000e)); //(0x3004, 0x000e) is grid scaling double gridScaling; if (findingsGridScaling.empty()) { MITK_ERROR << "Could not find DoseGridScaling tag" << std::endl; return result; } else { gridScaling = boost::lexical_cast(findingsGridScaling.front().value); } AccessByItk_1(originalImage, MultiplyGridScaling, gridScaling); auto statistics = this->scaledDoseImage->GetStatistics(); double maxDose = statistics->GetScalarValueMax(); this->scaledDoseImage->SetPropertyList(originalImage->GetPropertyList()); this->scaledDoseImage->SetProperty(mitk::RTConstants::PRESCRIBED_DOSE_PROPERTY_NAME.c_str(), mitk::GenericProperty::New(0.8*maxDose)); auto findings = ExtractPathsOfInterest(tagsOfInterestList, frames); SetProperties(this->scaledDoseImage, findings); result.push_back(this->scaledDoseImage.GetPointer()); return result; } RTDoseReaderService* RTDoseReaderService::Clone() const { return new RTDoseReaderService(*this); } } diff --git a/Modules/DicomRT/autoload/IO/mitkRTDoseReaderService.h b/Modules/DicomRT/autoload/IO/mitkRTDoseReaderService.h index 112f8da28c..ad24fd6011 100644 --- a/Modules/DicomRT/autoload/IO/mitkRTDoseReaderService.h +++ b/Modules/DicomRT/autoload/IO/mitkRTDoseReaderService.h @@ -1,64 +1,66 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkRTDoseReaderService_h #define mitkRTDoseReaderService_h #include #include #include #include namespace mitk { /** * \brief RTDoseReaderService reads DICOM files of modality RTDOSE. */ class MITKDICOMRTIO_EXPORT RTDoseReaderService : public mitk::AbstractFileReader { public: RTDoseReaderService(const RTDoseReaderService& other); RTDoseReaderService(); ~RTDoseReaderService() override; using AbstractFileReader::Read; - /** - * @brief Reads a dicom dataset from a RTDOSE file - * The method reads the PixelData from the DicomRT dose file and scales - * them with a factor for getting Gray-values instead of pixel-values. - * The Gray-values are stored in a mitkImage with a vtkColorTransferFunc. - * Relative values are used for coloring the image. The relative values are - * relative to a PrescriptionDose defined in the RT-Plan. If there is no - * RT-Plan file PrescriptionDose is set to 80% of the maximum dose. - */ - std::vector > Read() override; + + protected: + /** + * @brief Reads a dicom dataset from a RTDOSE file + * The method reads the PixelData from the DicomRT dose file and scales + * them with a factor for getting Gray-values instead of pixel-values. + * The Gray-values are stored in a mitkImage with a vtkColorTransferFunc. + * Relative values are used for coloring the image. The relative values are + * relative to a PrescriptionDose defined in the RT-Plan. If there is no + * RT-Plan file PrescriptionDose is set to 80% of the maximum dose. + */ + std::vector> DoRead() override; private: RTDoseReaderService* Clone() const override; /** * \brief Scales an image with a factor * * \param gridscale the factor to scale with */ template void MultiplyGridScaling(itk::Image< TPixel, VImageDimension>* image, float gridscale); mitk::Image::Pointer scaledDoseImage; us::ServiceRegistration m_FileReaderServiceReg; }; } #endif diff --git a/Modules/DicomRT/autoload/IO/mitkRTPlanReaderService.cpp b/Modules/DicomRT/autoload/IO/mitkRTPlanReaderService.cpp index d5d2879a6c..c1d8686b03 100644 --- a/Modules/DicomRT/autoload/IO/mitkRTPlanReaderService.cpp +++ b/Modules/DicomRT/autoload/IO/mitkRTPlanReaderService.cpp @@ -1,82 +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 #include #include #include #include #include #include namespace mitk { RTPlanReaderService::RTPlanReaderService() : AbstractFileReader(CustomMimeType(mitk::DicomRTMimeTypes::DICOMRT_PLAN_MIMETYPE_NAME()), mitk::DicomRTMimeTypes::DICOMRT_PLAN_MIMETYPE_DESCRIPTION()) { m_FileReaderServiceReg = RegisterService(); } RTPlanReaderService::RTPlanReaderService(const RTPlanReaderService& other) : mitk::AbstractFileReader(other) { } RTPlanReaderService::~RTPlanReaderService() {} - std::vector > RTPlanReaderService::Read() + std::vector > RTPlanReaderService::DoRead() { std::vector > result; auto DICOMTagsOfInterestService = GetDicomTagsOfInterestService(); auto tagsOfInterest = DICOMTagsOfInterestService->GetTagsOfInterest(); DICOMTagPathList tagsOfInterestList; for (const auto& tag : tagsOfInterest) { tagsOfInterestList.push_back(tag.first); } std::string location = GetInputLocation(); mitk::StringList files = { location }; mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); scanner->SetInputFiles(files); scanner->AddTagPaths(tagsOfInterestList); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); if (frames.empty()) { MITK_ERROR << "Error reading the RTPLAN file" << std::endl; return result; } auto findings = ExtractPathsOfInterest(tagsOfInterestList, frames); //just create empty image. No image information available in RTPLAN. But properties will be attached. Image::Pointer dummyImage = Image::New(); mitk::PixelType pt = mitk::MakeScalarPixelType(); unsigned int dim[] = { 1,1}; dummyImage->Initialize(pt, 2, dim); SetProperties(dummyImage, findings); result.push_back(dummyImage.GetPointer()); return result; } RTPlanReaderService* RTPlanReaderService::Clone() const { return new RTPlanReaderService(*this); } } diff --git a/Modules/DicomRT/autoload/IO/mitkRTPlanReaderService.h b/Modules/DicomRT/autoload/IO/mitkRTPlanReaderService.h index dd29b2af7d..c2653f2991 100644 --- a/Modules/DicomRT/autoload/IO/mitkRTPlanReaderService.h +++ b/Modules/DicomRT/autoload/IO/mitkRTPlanReaderService.h @@ -1,61 +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 mitkRTPlanReaderService_h #define mitkRTPlanReaderService_h #include "mitkAbstractFileReader.h" #include #include "mitkIDICOMTagsOfInterest.h" #include "mitkDICOMDatasetAccessingImageFrameInfo.h" #include #include "MitkDicomRTIOExports.h" namespace mitk { /** * \brief RTPlanReaderService reads DICOM files of modality RTPLAN. \details The tags are defined in mitk::GetDefaultDICOMTagsOfInterest() in Module MitkDicomReader. They are stored as TemporoSpatialStringProperty. with the key as their respective DICOM tags. \note No image information is in RTPLAN. */ class MITKDICOMRTIO_EXPORT RTPlanReaderService : public mitk::AbstractFileReader { public: RTPlanReaderService(); RTPlanReaderService(const RTPlanReaderService& other); using AbstractFileReader::Read; + + ~RTPlanReaderService() override; + + protected: /** * \brief Reads the file (only tags). @details DICOMDCMTKTagScanner is used to read the tags \note No image information is in RTPLAN. \sa mitk::GetDefaultDICOMTagsOfInterest() for tags that are read */ - std::vector > Read() override; - - ~RTPlanReaderService() override; + std::vector> DoRead() override; private: RTPlanReaderService* Clone() const override; us::ServiceRegistration m_FileReaderServiceReg; }; } #endif diff --git a/Modules/DicomRT/autoload/IO/mitkRTStructureSetReaderService.cpp b/Modules/DicomRT/autoload/IO/mitkRTStructureSetReaderService.cpp index ef2a5132ef..f4c4bcb794 100644 --- a/Modules/DicomRT/autoload/IO/mitkRTStructureSetReaderService.cpp +++ b/Modules/DicomRT/autoload/IO/mitkRTStructureSetReaderService.cpp @@ -1,288 +1,288 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkRTStructureSetReaderService.h" #include #include #include #include #include "dcmtk/dcmrt/drtstrct.h" namespace mitk { RTStructureSetReaderService::RTStructureSetReaderService() : AbstractFileReader(CustomMimeType(mitk::DicomRTMimeTypes::DICOMRT_STRUCT_MIMETYPE_NAME()), mitk::DicomRTMimeTypes::DICOMRT_STRUCT_MIMETYPE_DESCRIPTION()) { m_FileReaderServiceReg = RegisterService(); } RTStructureSetReaderService::RTStructureSetReaderService(const RTStructureSetReaderService& other) : mitk::AbstractFileReader(other) { } RTStructureSetReaderService::~RTStructureSetReaderService() {} RTStructureSetReaderService::RoiEntry::RoiEntry() { Number = 0; DisplayColor[0] = 1.0; DisplayColor[1] = 0.0; DisplayColor[2] = 0.0; ContourModelSet = mitk::ContourModelSet::New(); } RTStructureSetReaderService::RoiEntry::RoiEntry(const RoiEntry& src) { Number = src.Number; Name = src.Name; Description = src.Description; DisplayColor[0] = src.DisplayColor[0]; DisplayColor[1] = src.DisplayColor[1]; DisplayColor[2] = src.DisplayColor[2]; ContourModelSet = mitk::ContourModelSet::New(); SetPolyData(src.ContourModelSet); } RTStructureSetReaderService::RoiEntry::~RoiEntry() {} RTStructureSetReaderService::RoiEntry& RTStructureSetReaderService:: RoiEntry::operator =(const RoiEntry& src) { Number = src.Number; Name = src.Name; Description = src.Description; DisplayColor[0] = src.DisplayColor[0]; DisplayColor[1] = src.DisplayColor[1]; DisplayColor[2] = src.DisplayColor[2]; SetPolyData(src.ContourModelSet); return (*this); } void RTStructureSetReaderService::RoiEntry:: SetPolyData(mitk::ContourModelSet::Pointer roiPolyData) { if (roiPolyData == this->ContourModelSet) { return; } this->ContourModelSet = roiPolyData; } size_t RTStructureSetReaderService::GetNumberOfROIs() const { return this->ROISequenceVector.size(); } RTStructureSetReaderService::RoiEntry* RTStructureSetReaderService:: FindRoiByNumber(unsigned int roiNum) { for (unsigned int i = 0; i < this->ROISequenceVector.size(); ++i) { if (this->ROISequenceVector[i].Number == roiNum) { return &this->ROISequenceVector[i]; } } return nullptr; } - std::vector > RTStructureSetReaderService::Read() + std::vector > RTStructureSetReaderService::DoRead() { std::vector > result; std::string location = GetInputLocation(); auto DICOMTagsOfInterestService = GetDicomTagsOfInterestService(); auto tagsOfInterest = DICOMTagsOfInterestService->GetTagsOfInterest(); DICOMTagPathList tagsOfInterestList; for (const auto& tag : tagsOfInterest) { tagsOfInterestList.push_back(tag.first); } mitk::DICOMDCMTKTagScanner::Pointer scanner = mitk::DICOMDCMTKTagScanner::New(); scanner->SetInputFiles({ location }); scanner->AddTagPaths(tagsOfInterestList); scanner->Scan(); mitk::DICOMDatasetAccessingImageFrameList frames = scanner->GetFrameInfoList(); if (frames.empty()) { MITK_ERROR << "Error reading the RTSTRUCT file" << std::endl; return result; } auto findings = ExtractPathsOfInterest(tagsOfInterestList, frames); DcmFileFormat file; OFCondition output = file.loadFile(location.c_str(), EXS_Unknown); if (output.bad()) { MITK_ERROR << "Can't read the file" << std::endl; return result; } DcmDataset* dataset = file.getDataset(); DRTStructureSetIOD structureSetObject; OFCondition outp = structureSetObject.read(*dataset); if (!outp.good()) { MITK_ERROR << "Error reading the file" << std::endl; return result; } DRTStructureSetROISequence& roiSequence = structureSetObject.getStructureSetROISequence(); if (!roiSequence.gotoFirstItem().good()) { MITK_ERROR << "Error reading the structure sequence" << std::endl; return result; } do { DRTStructureSetROISequence::Item& currentSequence = roiSequence.getCurrentItem(); if (!currentSequence.isValid()) { continue; } OFString roiName; OFString roiDescription; Sint32 roiNumber; RoiEntry roi; currentSequence.getROIName(roiName); currentSequence.getROIDescription(roiDescription); currentSequence.getROINumber(roiNumber); roi.Name = roiName.c_str(); roi.Description = roiDescription.c_str(); roi.Number = roiNumber; this->ROISequenceVector.push_back(roi); } while (roiSequence.gotoNextItem().good()); Sint32 refRoiNumber; DRTROIContourSequence& roiContourSeqObject = structureSetObject.getROIContourSequence(); if (!roiContourSeqObject.gotoFirstItem().good()) { MITK_ERROR << "Error reading the contour sequence" << std::endl; return result; } do { mitk::ContourModelSet::Pointer contourSet = mitk::ContourModelSet::New(); DRTROIContourSequence::Item& currentRoiObject = roiContourSeqObject.getCurrentItem(); if (!currentRoiObject.isValid()) { continue; } currentRoiObject.getReferencedROINumber(refRoiNumber); DRTContourSequence& contourSeqObject = currentRoiObject.getContourSequence(); if (contourSeqObject.getNumberOfItems() > 0 && contourSeqObject.gotoFirstItem().good()) { do { DRTContourSequence::Item& contourItem = contourSeqObject.getCurrentItem(); if (!contourItem.isValid()) { continue; } OFString contourNumber; OFString numberOfPoints; OFVector contourData_LPS; mitk::ContourModel::Pointer contourSequence = mitk::ContourModel::New(); contourItem.getContourNumber(contourNumber); contourItem.getNumberOfContourPoints(numberOfPoints); contourItem.getContourData(contourData_LPS); for (unsigned int i = 0; i < contourData_LPS.size() / 3; i++) { mitk::Point3D point; point[0] = contourData_LPS.at(3 * i); point[1] = contourData_LPS.at(3 * i + 1); point[2] = contourData_LPS.at(3 * i + 2); contourSequence->AddVertex(point); } contourSequence->Close(); contourSet->AddContourModel(contourSequence); } while (contourSeqObject.gotoNextItem().good()); } else { MITK_WARN << "contourSeqObject has no items in sequence. Object is neglected and not read. Struct name: " << this->FindRoiByNumber(refRoiNumber)->Name << std::endl; } RoiEntry* refROI = this->FindRoiByNumber(refRoiNumber); if (refROI == nullptr) { MITK_ERROR << "Can not find references ROI" << std::endl; continue; } Sint32 roiColor; for (unsigned int j = 0; j < 3; j++) { currentRoiObject.getROIDisplayColor(roiColor, j); refROI->DisplayColor[j] = roiColor / 255.0; } refROI->ContourModelSet = contourSet; contourSet->SetProperty("name", mitk::StringProperty::New(refROI->Name)); contourSet->SetProperty("contour.color", mitk::ColorProperty::New( refROI->DisplayColor[0], refROI->DisplayColor[1], refROI->DisplayColor[2])); } while (roiContourSeqObject.gotoNextItem().good()); for (auto const& aROI : ROISequenceVector) { result.push_back(aROI.ContourModelSet.GetPointer()); result.at(result.size() - 1)->SetProperty("name", aROI.ContourModelSet->GetProperty("name")); result.at(result.size() - 1)->SetProperty("color", aROI.ContourModelSet->GetProperty("contour.color")); result.at(result.size() - 1)->SetProperty("contour.color", aROI.ContourModelSet->GetProperty("contour.color")); SetProperties(result.at(result.size() - 1).GetPointer(), findings); } return result; } RTStructureSetReaderService* RTStructureSetReaderService::Clone() const { return new RTStructureSetReaderService(*this); } } diff --git a/Modules/DicomRT/autoload/IO/mitkRTStructureSetReaderService.h b/Modules/DicomRT/autoload/IO/mitkRTStructureSetReaderService.h index 8986c6366f..8fcf2e8efb 100644 --- a/Modules/DicomRT/autoload/IO/mitkRTStructureSetReaderService.h +++ b/Modules/DicomRT/autoload/IO/mitkRTStructureSetReaderService.h @@ -1,85 +1,87 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKRTSTRUCTURESETREADER_H #define MITKRTSTRUCTURESETREADER_H #include #include "MitkDicomRTIOExports.h" #include #include namespace mitk { class MITKDICOMRTIO_EXPORT RTStructureSetReaderService : public mitk::AbstractFileReader { /** * Represent a region of interest (ROI) */ class RoiEntry { public: RoiEntry(); RoiEntry(const RoiEntry& src); virtual ~RoiEntry(); RoiEntry& operator=(const RoiEntry& src); void SetPolyData(ContourModelSet::Pointer roiPolyData); unsigned int Number; std::string Name; std::string Description; double DisplayColor[3]; mitk::ContourModelSet::Pointer ContourModelSet; }; public: RTStructureSetReaderService(); RTStructureSetReaderService(const RTStructureSetReaderService& other); ~RTStructureSetReaderService() override; /** * @brief Reading a RT StructureSet from the DICOM file and returns the ROIs * (region of interest) as a ContourModelSet. One ContourModelSet represent * one ROI. A ContourModelSet contains ContourModels which represent the * single structures. */ using AbstractFileReader::Read; - std::vector > Read() override; + + protected: + std::vector> DoRead() override; private: RTStructureSetReaderService* Clone() const override; /** * containing the ROIs meta information like name number and description */ std::vector ROISequenceVector; /** * Returns the number of ROIs from the ROISequenceVector */ size_t GetNumberOfROIs() const; /** * Returns the relevant ROI from the ROISequenceVector by its number */ RoiEntry* FindRoiByNumber(unsigned int roiNum); us::ServiceRegistration m_FileReaderServiceReg; }; } #endif // MITKRTSTRUCTURESETREADER_H diff --git a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.cpp b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.cpp index 3587fc8277..1a5362d805 100644 --- a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.cpp +++ b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.cpp @@ -1,169 +1,169 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK #include "mitkNavigationDataReaderCSV.h" #include #include // STL #include mitk::NavigationDataReaderCSV::NavigationDataReaderCSV() : AbstractFileReader( mitk::IGTMimeTypes::NAVIGATIONDATASETCSV_MIMETYPE(), "MITK NavigationData Reader (CSV)") { RegisterService(); } mitk::NavigationDataReaderCSV::NavigationDataReaderCSV(const mitk::NavigationDataReaderCSV& other) : AbstractFileReader(other) { } mitk::NavigationDataReaderCSV::~NavigationDataReaderCSV() { } mitk::NavigationDataReaderCSV* mitk::NavigationDataReaderCSV::Clone() const { return new NavigationDataReaderCSV(*this); } -std::vector> mitk::NavigationDataReaderCSV::Read() +std::vector> mitk::NavigationDataReaderCSV::DoRead() { std::vector fileContent = GetFileContentLineByLine(GetInputLocation()); int NumOfTools = getNumberOfToolsInLine(fileContent[0]); mitk::NavigationDataSet::Pointer returnValue = mitk::NavigationDataSet::New(NumOfTools); std::vector result; result.push_back(returnValue.GetPointer()); // start from line 1 to leave out header for (unsigned int i = 1; iAddNavigationDatas(parseLine(fileContent[i], NumOfTools)); } return result; } int mitk::NavigationDataReaderCSV::getNumberOfToolsInLine(std::string line) { std::vector tokens=splitLine(line); int size = tokens.size(); int NumOfTools = (size)/9; if ( (size)%9 != 0 ) { MITK_ERROR("mitkNavigationDataReader") << "Illegal csv-file! Unexpected number of columns found! Assuming " << NumOfTools << " tools!"; } return NumOfTools ; } std::vector mitk::NavigationDataReaderCSV::splitLine(std::string line) { std::vector elems; std::stringstream ss(line); std::string item; while (std::getline(ss, item, ';')) { elems.push_back(item); } return elems; } mitk::NavigationData::Pointer mitk::NavigationDataReaderCSV::CreateNd(std::string timestamp, std::string valid, std::string X, std::string Y, std::string Z, std::string QX, std::string QY, std::string QZ, std::string QR) { mitk::NavigationData::Pointer result= mitk::NavigationData::New(); mitk::Point3D position; mitk::Quaternion orientation; bool isValid = false; double time; time = StringToDouble(timestamp); if (valid == "1") isValid = true; else isValid = false; position[0] = StringToDouble(X); position[1] = StringToDouble(Y); position[2] = StringToDouble(Z); orientation[0] = StringToDouble(QX); orientation[1] = StringToDouble(QY); orientation[2] = StringToDouble(QZ); orientation[3] = StringToDouble(QR); result->SetIGTTimeStamp(time); result->SetDataValid(isValid); result->SetPosition(position); result->SetOrientation(orientation); return result; } double mitk::NavigationDataReaderCSV::StringToDouble( const std::string& s ) { std::istringstream i(s); double x; if (!(i >> x)) return 0; return x; } std::vector mitk::NavigationDataReaderCSV::parseLine(std::string line, int NumOfTools) { std::vector parts = splitLine(line); std::vector result; for (int n = 0; n < NumOfTools; n++) { mitk::NavigationData::Pointer nd; int offset = n * 9; nd = CreateNd(parts[offset], parts[offset + 1], parts[offset + 2], parts[offset + 3], parts[offset + 4], parts[offset + 5], parts[offset + 6], parts[offset + 7], parts[offset + 8]); result.push_back(nd); } return result; } std::vector mitk::NavigationDataReaderCSV::GetFileContentLineByLine(std::string filename) { std::vector readData = std::vector(); //define own locale mitk::LocaleSwitch localeSwitch("C"); //read file std::ifstream file; file.open(filename.c_str(), std::ios::in); if (file.good()) { //read out file file.seekg(0L, std::ios::beg); // move to begin of file while (! file.eof()) { std::string buffer; std::getline(file,buffer); // read out file line by line if (buffer.size() > 0) readData.push_back(buffer); } } file.close(); return readData; } diff --git a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.h b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.h index 7a9527c492..462a6b4ca1 100644 --- a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.h +++ b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderCSV.h @@ -1,80 +1,80 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 MITKNavigationDataReaderCSV_H_HEADER_INCLUDED_ #define MITKNavigationDataReaderCSV_H_HEADER_INCLUDED_ #include #include #include namespace mitk { /** This class reads csv logged navigation datas from the hard disc and returns * the navigation data set. * * Caution: at the moment only one navigation data is supported which means that only * the data of the first navigation tool in the file is read! */ class MITKIGTIO_EXPORT NavigationDataReaderCSV : public AbstractFileReader { public: NavigationDataReaderCSV(); ~NavigationDataReaderCSV() override; /** @return Returns the NavigationDataSet of the first tool in the given file. * Returns an empty NavigationDataSet if the file could not be read. */ using AbstractFileReader::Read; - std::vector> Read() override; protected: + std::vector> DoRead() override; /** * /brief Creates a NavigationData Pointer based on the given Input. */ mitk::NavigationData::Pointer CreateNd(std::string timestamp, std::string valid, std::string X, std::string Y, std::string Z, std::string QX, std::string QY, std::string QZ, std::string QR); /** * /brief Presents File Content line by line */ std::vector GetFileContentLineByLine(std::string filename); /** * /brief Calculates the Number of Tools based on the number of colums per line. */ int getNumberOfToolsInLine(std::string line); /** * /brief Converts string to double returns zero if failing */ std::vector parseLine(std::string line, int NumOfTools); /** * /brief Converts string to double returns zero if failing */ double StringToDouble( const std::string& s ); /** * /brief Split line in elemens based on a given delim */ std::vector splitLine(std::string line); NavigationDataReaderCSV(const NavigationDataReaderCSV& other); mitk::NavigationDataReaderCSV* Clone() const override; }; } #endif // MITKNavigationDataReaderCSV_H_HEADER_INCLUDED_ diff --git a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.cpp b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.cpp index 004ed0a49a..24551b7f0a 100644 --- a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.cpp +++ b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.cpp @@ -1,364 +1,364 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK #include "mitkNavigationDataReaderXML.h" #include #include // Third Party #include #include #include mitk::NavigationDataReaderXML::NavigationDataReaderXML() : AbstractFileReader( mitk::IGTMimeTypes::NAVIGATIONDATASETXML_MIMETYPE(), "MITK NavigationData Reader (XML)"), m_parentElement(nullptr), m_currentNode(nullptr) { RegisterService(); } mitk::NavigationDataReaderXML::~NavigationDataReaderXML() { } mitk::NavigationDataReaderXML::NavigationDataReaderXML(const mitk::NavigationDataReaderXML& other) : AbstractFileReader(other), m_parentElement(nullptr), m_currentNode(nullptr) { } mitk::NavigationDataReaderXML* mitk::NavigationDataReaderXML::Clone() const { return new NavigationDataReaderXML(*this); } -std::vector> mitk::NavigationDataReaderXML::Read() +std::vector> mitk::NavigationDataReaderXML::DoRead() { mitk::NavigationDataSet::Pointer dataset; std::istream* in = GetInputStream(); if (in == nullptr) { dataset = Read(GetInputLocation()); } else { dataset = Read(in); } std::vector result; mitk::BaseData::Pointer base = dataset.GetPointer(); result.push_back(base); return result; } mitk::NavigationDataSet::Pointer mitk::NavigationDataReaderXML::Read(std::string fileName) { //define own locale mitk::LocaleSwitch localeSwitch("C"); m_FileName = fileName; TiXmlDocument document; if (!document.LoadFile(fileName)) { mitkThrowException(mitk::IGTIOException) << "File '" << fileName << "' could not be loaded."; } TiXmlElement* m_DataElem = document.FirstChildElement("Version"); if (!m_DataElem) { // for backwards compatibility of version tag m_DataElem = document.FirstChildElement("Data"); if (!m_DataElem) { mitkThrowException(mitk::IGTIOException) << "Data element not found."; } } if (m_DataElem->QueryIntAttribute("Ver", &m_FileVersion) != TIXML_SUCCESS) { if (m_DataElem->QueryIntAttribute("version", &m_FileVersion) != TIXML_SUCCESS) { mitkThrowException(mitk::IGTIOException) << "Version not specified in XML file."; } } if (m_FileVersion != 1) { mitkThrowException(mitk::IGTIOException) << "File format version " << m_FileVersion << " is not supported."; } m_parentElement = document.FirstChildElement("Data"); if (!m_parentElement) { mitkThrowException(mitk::IGTIOException) << "Data element not found."; } m_parentElement->QueryIntAttribute("ToolCount", &m_NumberOfOutputs); mitk::NavigationDataSet::Pointer navigationDataSet = this->ReadNavigationDataSet(); return navigationDataSet; } mitk::NavigationDataSet::Pointer mitk::NavigationDataReaderXML::Read(std::istream* stream) { //define own locale mitk::LocaleSwitch localeSwitch("C"); // first get the file version m_FileVersion = this->GetFileVersion(stream); // check if we have a valid version: m_FileVersion has to be always bigger than 1 for playing if (m_FileVersion < 1) { StreamInvalid("Playing not possible. Invalid file version!"); return nullptr; } m_NumberOfOutputs = this->GetNumberOfNavigationDatas(stream); if (m_NumberOfOutputs == 0) { return nullptr; } mitk::NavigationDataSet::Pointer dataSet = this->ReadNavigationDataSet(); return dataSet; } mitk::NavigationDataSet::Pointer mitk::NavigationDataReaderXML::ReadNavigationDataSet() { mitk::NavigationDataSet::Pointer navigationDataSet = mitk::NavigationDataSet::New(m_NumberOfOutputs); mitk::NavigationData::Pointer curNavigationData; do { std::vector navDatas(m_NumberOfOutputs); for (int n = 0; n < m_NumberOfOutputs; ++n) { curNavigationData = this->ReadVersion1(); if (curNavigationData.IsNull()) { if (n != 0) { MITK_WARN("mitkNavigationDataReaderXML") << "Different number of NavigationData objects for different tools. Ignoring last ones."; } break; } navDatas.at(n) = curNavigationData; } if (curNavigationData.IsNotNull()) { navigationDataSet->AddNavigationDatas(navDatas); } } while (curNavigationData.IsNotNull()); return navigationDataSet; } mitk::NavigationData::Pointer mitk::NavigationDataReaderXML::ReadVersion1() { if ( !m_parentElement ) { mitkThrowException(mitk::IGTIOException) << "Reading XML is not possible. Parent element is not set."; } TiXmlElement* elem; m_currentNode = m_parentElement->IterateChildren(m_currentNode); bool delElem; if(m_currentNode) { elem = m_currentNode->ToElement(); if(elem==nullptr) { mitkThrowException(mitk::IGTException) << "Cannot find element: Is this file damaged?"; } delElem = false; } else { elem = new TiXmlElement(""); delElem = true; } mitk::NavigationData::Pointer nd = this->ReadNavigationData(elem); if(delElem) { delete elem; } return nd; } mitk::NavigationData::Pointer mitk::NavigationDataReaderXML::ReadNavigationData(TiXmlElement* elem) { if (elem == nullptr) {mitkThrow() << "Error: Element is nullptr!";} mitk::NavigationData::Pointer nd = mitk::NavigationData::New(); mitk::NavigationData::PositionType position; mitk::NavigationData::OrientationType orientation(0.0,0.0,0.0,0.0); mitk::NavigationData::TimeStampType timestamp = -1; mitk::NavigationData::CovarianceMatrixType matrix; bool hasPosition = true; bool hasOrientation = true; bool dataValid = false; position.Fill(0.0); matrix.SetIdentity(); elem->QueryDoubleAttribute("Time",×tamp); if (timestamp == -1) { return nullptr; //the calling method should check the return value if it is valid/not nullptr } elem->QueryDoubleAttribute("X", &position[0]); elem->QueryDoubleAttribute("Y", &position[1]); elem->QueryDoubleAttribute("Z", &position[2]); elem->QueryDoubleAttribute("QX", &orientation[0]); elem->QueryDoubleAttribute("QY", &orientation[1]); elem->QueryDoubleAttribute("QZ", &orientation[2]); elem->QueryDoubleAttribute("QR", &orientation[3]); elem->QueryDoubleAttribute("C00", &matrix[0][0]); elem->QueryDoubleAttribute("C01", &matrix[0][1]); elem->QueryDoubleAttribute("C02", &matrix[0][2]); elem->QueryDoubleAttribute("C03", &matrix[0][3]); elem->QueryDoubleAttribute("C04", &matrix[0][4]); elem->QueryDoubleAttribute("C05", &matrix[0][5]); elem->QueryDoubleAttribute("C10", &matrix[1][0]); elem->QueryDoubleAttribute("C11", &matrix[1][1]); elem->QueryDoubleAttribute("C12", &matrix[1][2]); elem->QueryDoubleAttribute("C13", &matrix[1][3]); elem->QueryDoubleAttribute("C14", &matrix[1][4]); elem->QueryDoubleAttribute("C15", &matrix[1][5]); int tmpval = 0; elem->QueryIntAttribute("Valid", &tmpval); if (tmpval == 0) dataValid = false; else dataValid = true; tmpval = 0; elem->QueryIntAttribute("hO", &tmpval); if (tmpval == 0) hasOrientation = false; else hasOrientation = true; tmpval = 0; elem->QueryIntAttribute("hP", &tmpval); if (tmpval == 0) hasPosition = false; else hasPosition = true; nd->SetIGTTimeStamp(timestamp); nd->SetPosition(position); nd->SetOrientation(orientation); nd->SetCovErrorMatrix(matrix); nd->SetDataValid(dataValid); nd->SetHasOrientation(hasOrientation); nd->SetHasPosition(hasPosition); return nd; } // -- deprecated | begin unsigned int mitk::NavigationDataReaderXML::GetFileVersion(std::istream* stream) { if (stream==nullptr) { MITK_ERROR << "No input stream set!"; mitkThrowException(mitk::IGTIOException)<<"No input stream set!"; } if (!stream->good()) { MITK_ERROR << "Stream is not good!"; mitkThrowException(mitk::IGTIOException)<<"Stream is not good!"; } int version = 1; auto dec = new TiXmlDeclaration(); *stream >> *dec; if(strcmp(dec->Version(),"") == 0) { MITK_ERROR << "The input stream seems to have XML incompatible format"; mitkThrowException(mitk::IGTIOException) << "The input stream seems to have XML incompatible format"; } m_parentElement = new TiXmlElement(""); *stream >> *m_parentElement; //2nd line this is the file version std::string tempValue = m_parentElement->Value(); if(tempValue != "Version") { if(tempValue == "Data"){ m_parentElement->QueryIntAttribute("version",&version); } } else { m_parentElement->QueryIntAttribute("Ver",&version); } if (version > 0) { return version; } else { return 0; } } unsigned int mitk::NavigationDataReaderXML::GetNumberOfNavigationDatas(std::istream* stream) { if (stream == nullptr) { MITK_ERROR << "No input stream set!"; mitkThrowException(mitk::IGTException)<<"No input stream set!"; } if (!stream->good()) { MITK_ERROR << "Stream not good!"; mitkThrowException(mitk::IGTException)<<"Stream not good!"; } //If something has changed in a future version of the XML definition e.g. navigationcount or addional parameters //catch this here with a select case block (see GenerateData() method) int numberOfTools = 0; std::string tempValue = m_parentElement->Value(); if(tempValue == "Version"){ *stream >> *m_parentElement; } m_parentElement->QueryIntAttribute("ToolCount",&numberOfTools); if (numberOfTools > 0) { return numberOfTools; } return 0; } void mitk::NavigationDataReaderXML::StreamInvalid(std::string message) { m_StreamEnd = true; m_ErrorMessage = message; m_StreamValid = false; mitkThrowException(mitk::IGTIOException) << "Invalid stream!"; } // -- deprecated | end diff --git a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.h b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.h index 5d5b5d81f4..8c6dc89b47 100644 --- a/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.h +++ b/Modules/IGTBase/autoload/IO/mitkNavigationDataReaderXML.h @@ -1,106 +1,106 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef MITKNavigationDataReaderXML_H_HEADER_INCLUDED_ #define MITKNavigationDataReaderXML_H_HEADER_INCLUDED_ #include #include #include // includes for exceptions #include #include class TiXmlElement; class TiXmlNode; namespace mitk { class MITKIGTIO_EXPORT NavigationDataReaderXML : public AbstractFileReader { public: NavigationDataReaderXML(); ~NavigationDataReaderXML() override; using AbstractFileReader::Read; - std::vector> Read() override; protected: + std::vector> DoRead() override; NavigationDataReaderXML(const NavigationDataReaderXML& other); mitk::NavigationDataReaderXML* Clone() const override; NavigationDataSet::Pointer ReadNavigationDataSet(); /** * \brief This method reads one line of the XML document and returns the data as a NavigationData object * If there is a new file version another method must be added which reads this data. * @throw mitk::IGTException Throws an exceptions if file is damaged. */ mitk::NavigationData::Pointer ReadVersion1(); mitk::NavigationData::Pointer ReadNavigationData(TiXmlElement* elem); std::string m_FileName; TiXmlElement* m_parentElement; TiXmlNode* m_currentNode; int m_FileVersion; ///< indicates which XML encoding is used int m_NumberOfOutputs; ///< stores the number of outputs known from the XML document // -- deprecated | begin //std::istream* m_Stream; ///< stores a pointer to the input stream bool m_StreamEnd; ///< stores if the input stream arrived at end bool m_StreamValid; ///< stores if the input stream is valid or not std::string m_ErrorMessage; ///< stores the error message if the stream is invalid /** * \brief Creates a stream out of the filename given by the variable m_FileName. * The stream is then set to m_Stream. * * @throw mitk::IGTIOException Throws an exception if file does not exist * @throw mitk::IGTException Throws an exception if the stream is nullptr */ //void CreateStreamFromFilename(); /** * \brief Returns the file version out of the XML document. * @throw mitk::IGTException Throws an mitk::IGTException an exception if stream is nullptr or not good. * @throw mitk::IGTIOException Throws an mitk::IGTIOException if the stream has an incompatible XML format. */ unsigned int GetFileVersion(std::istream* stream); /** * \brief Returns the number of tracked tools out of the XML document. * @throw Throws an exception if stream is nullptr. * @throw Throws an exception if the input stream has an XML incompatible format. */ unsigned int GetNumberOfNavigationDatas(std::istream* stream); /** * @brief This is a helping method which gives an error message and throws an exception with the given message. * It can be used if a stream is found to be invalid. * * @throw mitk::IGTIOException Always throws an exception. */ void StreamInvalid(std::string message); ///< help method which sets the stream invalid and displays an error // -- deprecated | end private: NavigationDataSet::Pointer Read(std::istream* stream); NavigationDataSet::Pointer Read(std::string fileName); }; } // namespace mitk #endif // MITKNavigationDataReaderXML_H_HEADER_INCLUDED_ diff --git a/Modules/IOExt/Internal/mitkObjFileReaderService.cpp b/Modules/IOExt/Internal/mitkObjFileReaderService.cpp index b60f99fb6d..f5c1919e3c 100644 --- a/Modules/IOExt/Internal/mitkObjFileReaderService.cpp +++ b/Modules/IOExt/Internal/mitkObjFileReaderService.cpp @@ -1,53 +1,53 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK #include "mitkObjFileReaderService.h" #include #include #include // VTK #include #include mitk::ObjFileReaderService::ObjFileReaderService() : AbstractFileReader(CustomMimeType(IOMimeTypes::WAVEFRONT_OBJ_MIMETYPE()), "Wavefront OBJ Reader") { this->RegisterService(); } mitk::ObjFileReaderService::~ObjFileReaderService() { } -std::vector> mitk::ObjFileReaderService::Read() +std::vector> mitk::ObjFileReaderService::DoRead() { std::vector> result; vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(GetInputLocation().c_str()); reader->Update(); if (reader->GetOutput() != nullptr) { mitk::Surface::Pointer surface = mitk::Surface::New(); surface->SetVtkPolyData(reader->GetOutput()); result.push_back(dynamic_cast(surface.GetPointer())); } return result; } mitk::ObjFileReaderService *mitk::ObjFileReaderService::Clone() const { return new ObjFileReaderService(*this); } diff --git a/Modules/IOExt/Internal/mitkObjFileReaderService.h b/Modules/IOExt/Internal/mitkObjFileReaderService.h index 6a059881c8..7a3f3919b2 100644 --- a/Modules/IOExt/Internal/mitkObjFileReaderService.h +++ b/Modules/IOExt/Internal/mitkObjFileReaderService.h @@ -1,45 +1,47 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 ObjFileReaderService_h #define ObjFileReaderService_h #include #include namespace mitk { class BaseData; /** * @brief Used to read surfaces from Wavefront OBJ files. * * @ingroup IOExt */ class ObjFileReaderService : public AbstractFileReader { public: ObjFileReaderService(); ~ObjFileReaderService() override; using AbstractFileReader::Read; - std::vector> Read() override; static mitk::CustomMimeType mimeType; + protected: + std::vector> DoRead() override; + private: ObjFileReaderService *Clone() const override; }; } // namespace mitk #endif /* ObjFileReaderService_h */ diff --git a/Modules/IOExt/Internal/mitkPlyFileReaderService.cpp b/Modules/IOExt/Internal/mitkPlyFileReaderService.cpp index 8f6bf4eb1a..bb44d3ed6a 100644 --- a/Modules/IOExt/Internal/mitkPlyFileReaderService.cpp +++ b/Modules/IOExt/Internal/mitkPlyFileReaderService.cpp @@ -1,53 +1,53 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK #include "mitkPlyFileReaderService.h" #include #include #include // VTK #include #include mitk::PlyFileReaderService::PlyFileReaderService() : AbstractFileReader(CustomMimeType(IOMimeTypes::STANFORD_PLY_MIMETYPE()), "Stanford Triangle PLY Reader") { this->RegisterService(); } mitk::PlyFileReaderService::~PlyFileReaderService() { } -std::vector> mitk::PlyFileReaderService::Read() +std::vector> mitk::PlyFileReaderService::DoRead() { std::vector> result; vtkSmartPointer reader = vtkSmartPointer::New(); reader->SetFileName(GetInputLocation().c_str()); reader->Update(); if (reader->GetOutput() != nullptr) { mitk::Surface::Pointer surface = mitk::Surface::New(); surface->SetVtkPolyData(reader->GetOutput()); result.push_back(dynamic_cast(surface.GetPointer())); } return result; } mitk::PlyFileReaderService *mitk::PlyFileReaderService::Clone() const { return new PlyFileReaderService(*this); } diff --git a/Modules/IOExt/Internal/mitkPlyFileReaderService.h b/Modules/IOExt/Internal/mitkPlyFileReaderService.h index ac81e403e6..52c1d6b19e 100644 --- a/Modules/IOExt/Internal/mitkPlyFileReaderService.h +++ b/Modules/IOExt/Internal/mitkPlyFileReaderService.h @@ -1,47 +1,49 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 PlyFileReaderService_h #define PlyFileReaderService_h #include #include namespace mitk { class BaseData; /** * @brief Used to read surfaces from the PLY format. * * This reader can read binary and ASCII versions of the format transparently. * * @ingroup IOExt */ class PlyFileReaderService : public AbstractFileReader { public: PlyFileReaderService(); ~PlyFileReaderService() override; using AbstractFileReader::Read; - std::vector> Read() override; static mitk::CustomMimeType mimeType; + protected: + std::vector> DoRead() override; + private: PlyFileReaderService *Clone() const override; }; } // namespace mitk #endif /* PlyFileReaderService_h */ diff --git a/Modules/IOExt/Internal/mitkSceneFileReader.cpp b/Modules/IOExt/Internal/mitkSceneFileReader.cpp index 121c6a3817..62ae6221f4 100644 --- a/Modules/IOExt/Internal/mitkSceneFileReader.cpp +++ b/Modules/IOExt/Internal/mitkSceneFileReader.cpp @@ -1,65 +1,81 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkSceneFileReader.h" #include #include #include +#include namespace mitk { SceneFileReader::SceneFileReader() : AbstractFileReader() { CustomMimeType mimeType(IOMimeTypes::DEFAULT_BASE_NAME() + ".scene"); mimeType.SetComment("MITK Scene Files"); mimeType.SetCategory("MITK Scenes"); mimeType.AddExtension("mitk"); this->SetDescription("MITK Scene Reader"); this->SetMimeType(mimeType); this->RegisterService(); } DataStorage::SetOfObjects::Pointer SceneFileReader::Read(DataStorage &ds) { // const DataStorage::SetOfObjects::STLContainerType& oldNodes = ds.GetAll()->CastToSTLConstContainer(); DataStorage::SetOfObjects::ConstPointer oldNodes = ds.GetAll(); SceneIO::Pointer sceneIO = SceneIO::New(); sceneIO->LoadScene(this->GetLocalFileName(), &ds, false); DataStorage::SetOfObjects::ConstPointer newNodes = ds.GetAll(); // Compute the difference DataStorage::SetOfObjects::Pointer result = DataStorage::SetOfObjects::New(); unsigned int index = 0; for (DataStorage::SetOfObjects::ConstIterator iter = newNodes->Begin(), iterEnd = newNodes->End(); iter != iterEnd; ++iter) { if (!oldNodes->empty()) { if (std::find(oldNodes->begin(), oldNodes->end(), iter.Value()) == oldNodes->end()) result->InsertElement(index++, iter.Value()); } else { result->InsertElement(index++, iter.Value()); } } return result; } - std::vector SceneFileReader::Read() { return AbstractFileReader::Read(); } + std::vector SceneFileReader::DoRead() + { + std::vector result; + + DataStorage::Pointer ds = StandaloneDataStorage::New().GetPointer(); + this->Read(*ds); + DataStorage::SetOfObjects::ConstPointer dataNodes = ds->GetAll(); + for (DataStorage::SetOfObjects::ConstIterator iter = dataNodes->Begin(), iterEnd = dataNodes->End(); + iter != iterEnd; + ++iter) + { + result.push_back(iter.Value()->GetData()); + } + return result; + } + SceneFileReader *SceneFileReader::Clone() const { return new SceneFileReader(*this); } } diff --git a/Modules/IOExt/Internal/mitkSceneFileReader.h b/Modules/IOExt/Internal/mitkSceneFileReader.h index e2c89774a3..efdc7c2f28 100644 --- a/Modules/IOExt/Internal/mitkSceneFileReader.h +++ b/Modules/IOExt/Internal/mitkSceneFileReader.h @@ -1,36 +1,38 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef SceneFileReader_H_HEADER_INCLUDED #define SceneFileReader_H_HEADER_INCLUDED // MITK #include namespace mitk { class SceneFileReader : public mitk::AbstractFileReader { public: SceneFileReader(); using AbstractFileReader::Read; - std::vector> Read() override; DataStorage::SetOfObjects::Pointer Read(DataStorage &ds) override; + protected: + std::vector> DoRead() override; + private: SceneFileReader *Clone() const override; }; } // namespace mitk #endif /* SceneFileReader_H_HEADER_INCLUDED_C1E7E521 */ diff --git a/Modules/IOExt/Internal/mitkVtkUnstructuredGridReader.cpp b/Modules/IOExt/Internal/mitkVtkUnstructuredGridReader.cpp index 13481b9963..6836dd7cd1 100644 --- a/Modules/IOExt/Internal/mitkVtkUnstructuredGridReader.cpp +++ b/Modules/IOExt/Internal/mitkVtkUnstructuredGridReader.cpp @@ -1,88 +1,88 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "mitkVtkUnstructuredGridReader.h" #include #include #include #include #include #include #include mitk::VtkUnstructuredGridReader::VtkUnstructuredGridReader() : AbstractFileReader() { CustomMimeType mimeType(IOMimeTypes::DEFAULT_BASE_NAME() + ".vtu"); mimeType.SetComment("Vtk Unstructured Grid Files"); mimeType.SetCategory("Vtk Unstructured Grid"); mimeType.AddExtension("vtu"); mimeType.AddExtension("vtk"); this->SetDescription("Vtk Unstructured Grid Files"); this->SetMimeType(mimeType); this->RegisterService(); } mitk::VtkUnstructuredGridReader::~VtkUnstructuredGridReader() { } -std::vector> mitk::VtkUnstructuredGridReader::Read() +std::vector> mitk::VtkUnstructuredGridReader::DoRead() { MITK_INFO << "Loading " << " as vtk unstructured grid"; std::vector> result; MITK_INFO << this->GetLocalFileName(); std::string ext = itksys::SystemTools::GetFilenameLastExtension(GetLocalFileName().c_str()); ext = itksys::SystemTools::LowerCase(ext); if (ext == ".vtk") { vtkDataReader *chooser = vtkDataReader::New(); chooser->SetFileName(GetLocalFileName().c_str()); if (chooser->IsFileUnstructuredGrid()) { vtkUnstructuredGridReader *reader = vtkUnstructuredGridReader::New(); reader->SetFileName(GetLocalFileName().c_str()); reader->Update(); if (reader->GetOutput() != nullptr) { mitk::UnstructuredGrid::Pointer grid = mitk::UnstructuredGrid::New(); grid->SetVtkUnstructuredGrid(reader->GetOutput()); result.push_back(grid.GetPointer()); } reader->Delete(); } } else if (ext == ".vtu") { vtkXMLUnstructuredGridReader *reader = vtkXMLUnstructuredGridReader::New(); reader->SetFileName(GetLocalFileName().c_str()); reader->Update(); if (reader->GetOutput() != nullptr) { mitk::UnstructuredGrid::Pointer grid = mitk::UnstructuredGrid::New(); grid->SetVtkUnstructuredGrid(reader->GetOutput()); result.push_back(grid.GetPointer()); } reader->Delete(); } return result; } mitk::VtkUnstructuredGridReader *mitk::VtkUnstructuredGridReader::Clone() const { return new mitk::VtkUnstructuredGridReader(*this); } diff --git a/Modules/IOExt/Internal/mitkVtkUnstructuredGridReader.h b/Modules/IOExt/Internal/mitkVtkUnstructuredGridReader.h index 1c9fa10f9f..122a715556 100644 --- a/Modules/IOExt/Internal/mitkVtkUnstructuredGridReader.h +++ b/Modules/IOExt/Internal/mitkVtkUnstructuredGridReader.h @@ -1,36 +1,37 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef VtkUnstructuredGridReader_H_HEADER_INCLUDED #define VtkUnstructuredGridReader_H_HEADER_INCLUDED #include namespace mitk { //##Documentation //## @brief Reader to read unstructured grid files in vtk-format class VtkUnstructuredGridReader : public AbstractFileReader { public: VtkUnstructuredGridReader(); ~VtkUnstructuredGridReader() override; using AbstractFileReader::Read; - std::vector> Read() override; protected: + std::vector> DoRead() override; + VtkUnstructuredGridReader *Clone() const override; }; } // namespace mitk #endif /* VtkUnstructuredGridReader_H_HEADER_INCLUDED */ diff --git a/Modules/ImageStatistics/Testing/files.cmake b/Modules/ImageStatistics/Testing/files.cmake index 2be45c6a95..6ca36d87ec 100644 --- a/Modules/ImageStatistics/Testing/files.cmake +++ b/Modules/ImageStatistics/Testing/files.cmake @@ -1,12 +1,13 @@ -set(MODULE_TESTS - mitkImageStatisticsCalculatorTest.cpp - mitkPointSetStatisticsCalculatorTest.cpp - mitkPointSetDifferenceStatisticsCalculatorTest.cpp - mitkImageStatisticsTextureAnalysisTest.cpp - mitkImageStatisticsContainerManagerTest.cpp -) - -set(MODULE_CUSTOM_TESTS - mitkImageStatisticsHotspotTest.cpp -# mitkMultiGaussianTest.cpp # TODO: activate test to generate new test cases for mitkImageStatisticsHotspotTest -) +set(MODULE_TESTS + mitkImageStatisticsCalculatorTest.cpp + mitkPointSetStatisticsCalculatorTest.cpp + mitkPointSetDifferenceStatisticsCalculatorTest.cpp + mitkImageStatisticsTextureAnalysisTest.cpp + mitkImageStatisticsContainerTest.cpp + mitkImageStatisticsContainerManagerTest.cpp +) + +set(MODULE_CUSTOM_TESTS + mitkImageStatisticsHotspotTest.cpp +# mitkMultiGaussianTest.cpp # TODO: activate test to generate new test cases for mitkImageStatisticsHotspotTest +) diff --git a/Modules/ImageStatistics/Testing/mitkImageStatisticsContainerTest.cpp b/Modules/ImageStatistics/Testing/mitkImageStatisticsContainerTest.cpp new file mode 100644 index 0000000000..c81d04373a --- /dev/null +++ b/Modules/ImageStatistics/Testing/mitkImageStatisticsContainerTest.cpp @@ -0,0 +1,378 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +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 "mitkTestingMacros.h" +#include "mitkTestFixture.h" + +//MITK includes +#include "mitkArbitraryTimeGeometry.h" +#include "mitkImageStatisticsContainerNodeHelper.h" +#include "mitkImageStatisticsContainerManager.h" +#include "mitkImageStatisticsContainer.h" + +class mitkImageStatisticsContainerTestSuite : public mitk::TestFixture +{ + CPPUNIT_TEST_SUITE(mitkImageStatisticsContainerTestSuite); + MITK_TEST(StatisticNamesIO); + MITK_TEST(InvalidKey); + MITK_TEST(TimeSteps); + MITK_TEST(PrintSelf); + MITK_TEST(InternalClone); + MITK_TEST(StatisticNames); + MITK_TEST(OverwriteStatistic); + MITK_TEST(AllStatisticNamesForContainer); + MITK_TEST(Reset); + CPPUNIT_TEST_SUITE_END(); + +private: + mitk::ImageStatisticsContainer::Pointer m_StatisticsContainer; + mitk::ImageStatisticsContainer::Pointer m_StatisticsContainer2; + mitk::ImageStatisticsContainer::Pointer m_StatisticsContainer3; + + mitk::ImageStatisticsContainer::ImageStatisticsObject m_StatisticsObject; + mitk::ImageStatisticsContainer::ImageStatisticsObject m_StatisticsObject2; + mitk::ImageStatisticsContainer::ImageStatisticsObject m_StatisticsObject3; + + mitk::ArbitraryTimeGeometry::Pointer m_TimeGeometry; + + 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::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_Geometry1MaxTP; + mitk::TimePointType m_Geometry2MaxTP; + mitk::TimePointType m_Geometry3MaxTP; + mitk::TimePointType m_Geometry3_5MaxTP; + mitk::TimePointType m_Geometry4MaxTP; + mitk::TimePointType m_Geometry5MaxTP; + + std::vector estimatedDefaultStatisticNames + { + "Mean", + "Median", + "StandardDeviation", + "RMS", + "Max", + "MaxPosition", + "Min", + "MinPosition", + "#Voxel", + "Volume [mm^3]", + "Skewness", + "Kurtosis", + "Uniformity", + "Entropy", + "MPP", + "UPP" + }; + +public: + void setUp() override + { + m_StatisticsContainer = mitk::ImageStatisticsContainer::New(); + m_StatisticsContainer2 = mitk::ImageStatisticsContainer::New(); + m_StatisticsContainer3 = mitk::ImageStatisticsContainer::New(); + + 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_TimeGeometry = mitk::ArbitraryTimeGeometry::New(); + m_TimeGeometry->ClearAllGeometries(); + m_TimeGeometry->AppendNewTimeStep(m_Geometry1, m_Geometry1MinTP, m_Geometry1MaxTP); + m_TimeGeometry->AppendNewTimeStep(m_Geometry2, m_Geometry2MinTP, m_Geometry2MaxTP); + m_TimeGeometry->AppendNewTimeStep(m_Geometry3, m_Geometry3MinTP, m_Geometry3MaxTP); + m_TimeGeometry->AppendNewTimeStep(m_Geometry4, m_Geometry4MinTP, m_Geometry4MaxTP); + m_TimeGeometry->AppendNewTimeStep(m_Geometry5, m_Geometry5MinTP, m_Geometry5MaxTP); + } + + void tearDown() override + { + m_StatisticsContainer->Reset(); + m_StatisticsContainer2->Reset(); + m_StatisticsContainer3->Reset(); + + m_StatisticsObject.Reset(); + m_StatisticsObject2.Reset(); + m_StatisticsObject3.Reset(); + + m_TimeGeometry->ClearAllGeometries(); + } + + void StatisticNamesIO() + { + auto defaultStatisticNames = mitk::ImageStatisticsContainer::ImageStatisticsObject::GetDefaultStatisticNames(); + for (size_t i = 0; i < estimatedDefaultStatisticNames.size(); i++) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("Estimated default statistics names are not correct.", defaultStatisticNames.at(i), estimatedDefaultStatisticNames.at(i)); + } + + std::string testName = "testName"; + double realTypeTest = 3.14; + m_StatisticsObject.AddStatistic(testName, realTypeTest); + auto customStatisticNames = m_StatisticsObject.GetCustomStatisticNames(); + size_t one = 1; + CPPUNIT_ASSERT_EQUAL_MESSAGE("Amount of estimated custom statistics names is not correct.", customStatisticNames.size(), one); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Estimated custom statistics names are not correct.", customStatisticNames.front(), testName); + + auto allStatisticNames = m_StatisticsObject.GetAllStatisticNames(); + estimatedDefaultStatisticNames.push_back(testName); + for (size_t i = 0; i < estimatedDefaultStatisticNames.size(); i++) + { + CPPUNIT_ASSERT_EQUAL_MESSAGE("Estimated statistics names are not correct.", allStatisticNames.at(i), estimatedDefaultStatisticNames.at(i)); + } + + auto existingStatisticNames = m_StatisticsObject.GetExistingStatisticNames(); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Amount of estimated existing statistics names is not correct.", existingStatisticNames.size(), one); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Estimated existing statistics names are not correct.", existingStatisticNames.front(), testName); + + m_StatisticsObject.Reset(); + CPPUNIT_ASSERT_MESSAGE("Custom statistics names were not removed correctly.", m_StatisticsObject.GetCustomStatisticNames().empty()); + } + + void InvalidKey() + { + CPPUNIT_ASSERT_THROW_MESSAGE("Exception should have been thrown because key does not exists.", m_StatisticsObject.GetValueNonConverted("Test"), mitk::Exception); + } + + void TimeSteps() + { + m_StatisticsContainer->SetTimeGeometry(m_TimeGeometry); + + m_StatisticsContainer->SetStatisticsForTimeStep(0, m_StatisticsObject); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Previous added time step was not saved correctly.", m_StatisticsContainer->TimeStepExists(0), true); + m_StatisticsContainer->SetStatisticsForTimeStep(1, m_StatisticsObject); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Previous added time step was not saved correctly.", m_StatisticsContainer->TimeStepExists(1), true); + m_StatisticsContainer->SetStatisticsForTimeStep(2, m_StatisticsObject); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Previous added time step was not saved correctly.", m_StatisticsContainer->TimeStepExists(2), true); + m_StatisticsContainer->SetStatisticsForTimeStep(3, m_StatisticsObject); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Previous added time step was not saved correctly.", m_StatisticsContainer->TimeStepExists(3), true); + m_StatisticsContainer->SetStatisticsForTimeStep(4, m_StatisticsObject); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Previous added time step was not saved correctly.", m_StatisticsContainer->TimeStepExists(4), true); + + CPPUNIT_ASSERT_THROW_MESSAGE("Out of timeStep geometry bounds timeStep was added but no exception was thrown.", m_StatisticsContainer->SetStatisticsForTimeStep(42, m_StatisticsObject), mitk::Exception); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistics container does not contain the right amount of timeSteps.", static_cast (m_StatisticsContainer->GetNumberOfTimeSteps()), 5); + + CPPUNIT_ASSERT_THROW_MESSAGE("A statistic for a non existing time steps was found.", m_StatisticsContainer->GetStatisticsForTimeStep(42), mitk::Exception); + } + + void PrintSelf() + { + std::stringstream print; + + // Check print function for empty container + CPPUNIT_ASSERT_NO_THROW_MESSAGE("Print function throws an exception.", m_StatisticsContainer->Print(print)); + + m_StatisticsContainer->SetTimeGeometry(m_TimeGeometry); + + m_StatisticsObject.AddStatistic("Test", 4.2); + + m_StatisticsContainer->SetStatisticsForTimeStep(0, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(1, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(2, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(3, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(4, m_StatisticsObject); + + // Check print function for filled container + CPPUNIT_ASSERT_NO_THROW_MESSAGE("Print function throws an exception.", m_StatisticsContainer->Print(print)); + } + + void InternalClone() + { + m_StatisticsContainer->SetTimeGeometry(m_TimeGeometry); + + m_StatisticsObject.AddStatistic("Test", 4.2); + + m_StatisticsContainer->SetStatisticsForTimeStep(0, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(1, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(2, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(3, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(4, m_StatisticsObject); + + auto clone = m_StatisticsContainer->Clone(); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", m_StatisticsContainer->GetNumberOfTimeSteps(), clone->GetNumberOfTimeSteps()); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->TimeStepExists(0), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->TimeStepExists(1), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->TimeStepExists(2), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->TimeStepExists(3), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->TimeStepExists(4), true); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->GetStatisticsForTimeStep(0).HasStatistic("Test"), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->GetStatisticsForTimeStep(1).HasStatistic("Test"), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->GetStatisticsForTimeStep(2).HasStatistic("Test"), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->GetStatisticsForTimeStep(3).HasStatistic("Test"), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Internal clone was not cloned correctly.", clone->GetStatisticsForTimeStep(4).HasStatistic("Test"), true); + } + + void StatisticNames() + { + m_StatisticsContainer->SetTimeGeometry(m_TimeGeometry); + m_StatisticsContainer2->SetTimeGeometry(m_TimeGeometry); + m_StatisticsContainer3->SetTimeGeometry(m_TimeGeometry); + + m_StatisticsObject.AddStatistic("Test", 4.2); + m_StatisticsObject2.AddStatistic("Test2", 4.2); + m_StatisticsObject3.AddStatistic("Test3", 4.2); + + // Setting the same statistics for different time steps will only add the statistics name once. + m_StatisticsContainer->SetStatisticsForTimeStep(0, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(1, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(2, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(3, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(4, m_StatisticsObject); + + // Setting the same statistics for different time steps will only add the statistics name once. + m_StatisticsContainer2->SetStatisticsForTimeStep(0, m_StatisticsObject2); + m_StatisticsContainer2->SetStatisticsForTimeStep(1, m_StatisticsObject2); + m_StatisticsContainer2->SetStatisticsForTimeStep(2, m_StatisticsObject2); + m_StatisticsContainer2->SetStatisticsForTimeStep(3, m_StatisticsObject2); + m_StatisticsContainer2->SetStatisticsForTimeStep(4, m_StatisticsObject2); + + // Setting the same statistics for different time steps will only add the statistics name once. + m_StatisticsContainer3->SetStatisticsForTimeStep(0, m_StatisticsObject3); + m_StatisticsContainer3->SetStatisticsForTimeStep(1, m_StatisticsObject3); + m_StatisticsContainer3->SetStatisticsForTimeStep(2, m_StatisticsObject3); + m_StatisticsContainer3->SetStatisticsForTimeStep(3, m_StatisticsObject3); + m_StatisticsContainer3->SetStatisticsForTimeStep(4, m_StatisticsObject3); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Estimated statistics names are not correct.", mitk::GetAllStatisticNames(m_StatisticsContainer).size(), estimatedDefaultStatisticNames.size() + 1); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Custom statistic name was not saved correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(0).HasStatistic("Test"), true); + + std::vector containers; + + containers.push_back(m_StatisticsContainer2.GetPointer()); + containers.push_back(m_StatisticsContainer3.GetPointer()); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Estimated statistics names are not correct.", mitk::GetAllStatisticNames(containers).size(), estimatedDefaultStatisticNames.size() + 2); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Custom statistic name was not saved correctly.", m_StatisticsContainer2->GetStatisticsForTimeStep(0).HasStatistic("Test2"), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Custom statistic name was not saved correctly.", m_StatisticsContainer3->GetStatisticsForTimeStep(0).HasStatistic("Test3"), true); + } + + void OverwriteStatistic() + { + m_StatisticsContainer->SetTimeGeometry(m_TimeGeometry); + + m_StatisticsObject.AddStatistic("Test", 4.2); + + m_StatisticsContainer->SetStatisticsForTimeStep(0, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(1, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(2, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(3, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(4, m_StatisticsObject); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic was not correctly added to StatisticsObject.", boost::get (m_StatisticsContainer->GetStatisticsForTimeStep(0).GetValueNonConverted("Test")), 4.2); + + // An existing statistic won't be updated by adding another statistic with same name to that object. + m_StatisticsObject.AddStatistic("Test", 42.0); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic was overwritten.", boost::get(m_StatisticsContainer->GetStatisticsForTimeStep(0).GetValueNonConverted("Test")), 4.2); + } + + void AllStatisticNamesForContainer() + { + m_StatisticsContainer->SetTimeGeometry(m_TimeGeometry); + + m_StatisticsObject.AddStatistic("Test", 4.2); + m_StatisticsObject2.AddStatistic("Test2", 4.2); + + // Setting the same statistics for different time steps will only add the statistics name once. + m_StatisticsContainer->SetStatisticsForTimeStep(0, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(1, m_StatisticsObject2); + m_StatisticsContainer->SetStatisticsForTimeStep(2, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(3, m_StatisticsObject2); + m_StatisticsContainer->SetStatisticsForTimeStep(4, m_StatisticsObject); + + std::vector containers; + + // Check if empty vector triggers exception. + CPPUNIT_ASSERT_NO_THROW_MESSAGE("Empty vector triggered an exception.", mitk::GetAllStatisticNames(containers)); + + containers.push_back(m_StatisticsContainer2.GetPointer()); + containers.push_back(m_StatisticsContainer3.GetPointer()); + + // Check if vector with empty container triggers exception. + CPPUNIT_ASSERT_NO_THROW_MESSAGE("Empty container triggered an exception.", mitk::GetAllStatisticNames(containers)); + + // Adding filled container to check if data was saved correctly. + containers.push_back(m_StatisticsContainer.GetPointer()); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Estimated statistics names are not correct.", mitk::GetAllStatisticNames(containers).size(), estimatedDefaultStatisticNames.size() + 2); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Custom statistic name was not saved correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(0).HasStatistic("Test"), true); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Custom statistic name was not saved correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(1).HasStatistic("Test2"), true); + } + + void Reset() + { + m_StatisticsContainer->SetTimeGeometry(m_TimeGeometry); + + m_StatisticsObject.AddStatistic("Test", 4.2); + + m_StatisticsContainer->SetStatisticsForTimeStep(0, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(1, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(2, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(3, m_StatisticsObject); + m_StatisticsContainer->SetStatisticsForTimeStep(4, m_StatisticsObject); + + m_StatisticsContainer->Reset(); + + m_StatisticsObject.Reset(); + + m_TimeGeometry->ClearAllGeometries(); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(0).GetExistingStatisticNames().size(), size_t()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(0).GetCustomStatisticNames().size(), size_t()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(1).GetExistingStatisticNames().size(), size_t()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(1).GetCustomStatisticNames().size(), size_t()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(2).GetExistingStatisticNames().size(), size_t()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(2).GetCustomStatisticNames().size(), size_t()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(3).GetExistingStatisticNames().size(), size_t()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(3).GetCustomStatisticNames().size(), size_t()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(4).GetExistingStatisticNames().size(), size_t()); + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic object for time step was not deleted correctly.", m_StatisticsContainer->GetStatisticsForTimeStep(4).GetCustomStatisticNames().size(), size_t()); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Statistic was not deleted correctly.", m_StatisticsObject.HasStatistic("Test"), false); + + CPPUNIT_ASSERT_EQUAL_MESSAGE("Time geometry was not cleared correctly.", m_TimeGeometry->IsValid(), false); + } +}; + +MITK_TEST_SUITE_REGISTRATION(mitkImageStatisticsContainer) diff --git a/Modules/IpPicSupportIO/Internal/mitkPicFileReader.cpp b/Modules/IpPicSupportIO/Internal/mitkPicFileReader.cpp index 2a5c956b32..61884011f1 100644 --- a/Modules/IpPicSupportIO/Internal/mitkPicFileReader.cpp +++ b/Modules/IpPicSupportIO/Internal/mitkPicFileReader.cpp @@ -1,299 +1,299 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkPicFileReader.h" #include "mitkPicHelper.h" #include "mitkCustomMimeType.h" #include "mitkImageWriteAccessor.h" #include static mitk::PixelType CastToPixelType(mitkIpPicType_t pictype, size_t bpe) { const bool isSignedIntegralType = (pictype == mitkIpPicInt); const bool isUnsignedIntegralType = (pictype == mitkIpPicUInt); if (isSignedIntegralType) { switch (bpe) { case sizeof(char) : return mitk::MakeScalarPixelType(); case sizeof(short) : return mitk::MakeScalarPixelType(); default: return mitk::MakeScalarPixelType(); } } else if (isUnsignedIntegralType) { switch (bpe) { case sizeof(unsigned char) : return mitk::MakeScalarPixelType(); case sizeof(unsigned short) : return mitk::MakeScalarPixelType(); default: return mitk::MakeScalarPixelType(); } } else // is floating point type { switch (bpe) { case sizeof(float) : return mitk::MakeScalarPixelType(); default: return mitk::MakeScalarPixelType(); } } } static mitk::ImageDescriptor::Pointer CastToImageDescriptor(mitkIpPicDescriptor *desc) { mitk::ImageDescriptor::Pointer imDescriptor = mitk::ImageDescriptor::New(); imDescriptor->Initialize(desc->n, desc->dim); mitk::PixelType ptype = ::CastToPixelType(desc->type, (desc->bpe / 8)); imDescriptor->AddNewChannel(ptype, "imported by pic"); return imDescriptor; } static mitkIpPicType_t CastToIpPicType(int intype) { const bool isSignedIntegralType = (intype == itk::ImageIOBase::INT || intype == itk::ImageIOBase::SHORT || intype == itk::ImageIOBase::CHAR || intype == itk::ImageIOBase::LONG); const bool isUnsignedIntegralType = (intype == itk::ImageIOBase::UINT || intype == itk::ImageIOBase::USHORT || intype == itk::ImageIOBase::UCHAR || intype == itk::ImageIOBase::ULONG); const bool isFloatingPointType = (intype == itk::ImageIOBase::FLOAT || intype == itk::ImageIOBase::DOUBLE); if (isSignedIntegralType) return mitkIpPicInt; if (isUnsignedIntegralType) return mitkIpPicUInt; if (isFloatingPointType) return mitkIpPicFloat; return mitkIpPicUnknown; } static mitkIpPicDescriptor * CastToIpPicDescriptor(mitk::Image::Pointer refImg, mitk::ImageWriteAccessor *imageAccess, mitkIpPicDescriptor *picDesc) { const mitk::ImageDescriptor::Pointer imDesc = refImg->GetImageDescriptor(); // initialize dimension information for (unsigned int i = 0; i < 8; i++) { picDesc->n[i] = 1; } // set dimension information picDesc->dim = refImg->GetDimension(); memcpy(picDesc->n, imDesc->GetDimensions(), picDesc->dim * sizeof(unsigned int)); picDesc->type = ::CastToIpPicType(refImg->GetPixelType().GetComponentType()); picDesc->bpe = refImg->GetPixelType().GetBpe(); if (imageAccess != nullptr) { picDesc->data = imageAccess->GetData(); } return picDesc; } mitk::PicFileReader::PicFileReader() : AbstractFileReader() { CustomMimeType mimeType(this->GetMimeTypePrefix() + "mbipic"); mimeType.AddExtension("pic"); mimeType.AddExtension("pic.gz"); mimeType.AddExtension("PIC"); mimeType.AddExtension("PIC.gz"); mimeType.SetCategory("Images"); mimeType.SetComment("DKFZ Legacy PIC Format"); this->SetMimeType(mimeType); this->SetDescription("DKFZ PIC"); this->RegisterService(); } -std::vector mitk::PicFileReader::Read() +std::vector mitk::PicFileReader::DoRead() { mitk::Image::Pointer image = this->CreateImage(); this->FillImage(image); std::vector result; result.push_back(image.GetPointer()); return result; } mitk::Image::Pointer mitk::PicFileReader::CreateImage() { Image::Pointer output = Image::New(); std::string fileName = this->GetLocalFileName(); mitkIpPicDescriptor *header = mitkIpPicGetHeader(fileName.c_str(), nullptr); if (!header) { mitkThrow() << "File could not be read."; } header = mitkIpPicGetTags(fileName.c_str(), header); int channels = 1; mitkIpPicTSV_t *tsv; if ((tsv = mitkIpPicQueryTag(header, "SOURCE HEADER")) != nullptr) { if (tsv->n[0] > 1e+06) { mitkIpPicTSV_t *tsvSH; tsvSH = mitkIpPicDelTag(header, "SOURCE HEADER"); mitkIpPicFreeTag(tsvSH); } } if ((tsv = mitkIpPicQueryTag(header, "ICON80x80")) != nullptr) { mitkIpPicTSV_t *tsvSH; tsvSH = mitkIpPicDelTag(header, "ICON80x80"); mitkIpPicFreeTag(tsvSH); } if ((tsv = mitkIpPicQueryTag(header, "VELOCITY")) != nullptr) { ++channels; mitkIpPicDelTag(header, "VELOCITY"); } if (header == nullptr || header->bpe == 0) { mitkThrow() << " Could not read file " << fileName; } // if pic image only 2D, the n[2] value is not initialized unsigned int slices = 1; if (header->dim == 2) { header->n[2] = slices; } // First initialize the geometry of the output image by the pic-header SlicedGeometry3D::Pointer slicedGeometry = mitk::SlicedGeometry3D::New(); PicHelper::InitializeEvenlySpaced(header, header->n[2], slicedGeometry); // if pic image only 3D, the n[3] value is not initialized unsigned int timesteps = 1; if (header->dim > 3) { timesteps = header->n[3]; } slicedGeometry->ImageGeometryOn(); ProportionalTimeGeometry::Pointer timeGeometry = ProportionalTimeGeometry::New(); timeGeometry->Initialize(slicedGeometry, timesteps); // Cast the pic descriptor to ImageDescriptor and initialize the output output->Initialize(CastToImageDescriptor(header)); output->SetTimeGeometry(timeGeometry); mitkIpPicFree(header); return output; } void mitk::PicFileReader::ConvertHandedness(mitkIpPicDescriptor *pic) { // left to right handed conversion if (pic->dim >= 3) { mitkIpPicDescriptor *slice = mitkIpPicCopyHeader(pic, nullptr); slice->dim = 2; size_t size = _mitkIpPicSize(slice); slice->data = malloc(size); size_t v, volumes = (pic->dim > 3 ? pic->n[3] : 1); size_t volume_size = size * pic->n[2]; for (v = 0; v < volumes; ++v) { auto *p_first = (unsigned char *)pic->data; auto *p_last = (unsigned char *)pic->data; p_first += v * volume_size; p_last += size * (pic->n[2] - 1) + v * volume_size; size_t i, smid = pic->n[2] / 2; for (i = 0; i < smid; ++i, p_last -= size, p_first += size) { memcpy(slice->data, p_last, size); memcpy(p_last, p_first, size); memcpy(p_first, slice->data, size); } } mitkIpPicFree(slice); } } mitk::PicFileReader *mitk::PicFileReader::Clone() const { return new PicFileReader(*this); } void mitk::PicFileReader::FillImage(Image::Pointer output) { mitkIpPicDescriptor *outputPic = mitkIpPicNew(); outputPic = CastToIpPicDescriptor(output, nullptr, outputPic); mitkIpPicDescriptor *pic = mitkIpPicGet(this->GetLocalFileName().c_str(), outputPic); // comes upside-down (in MITK coordinates) from PIC file ConvertHandedness(pic); mitkIpPicTSV_t *tsv; if ((tsv = mitkIpPicQueryTag(pic, "SOURCE HEADER")) != nullptr) { if (tsv->n[0] > 1e+06) { mitkIpPicTSV_t *tsvSH; tsvSH = mitkIpPicDelTag(pic, "SOURCE HEADER"); mitkIpPicFreeTag(tsvSH); } } if ((tsv = mitkIpPicQueryTag(pic, "ICON80x80")) != nullptr) { mitkIpPicTSV_t *tsvSH; tsvSH = mitkIpPicDelTag(pic, "ICON80x80"); mitkIpPicFreeTag(tsvSH); } if ((tsv = mitkIpPicQueryTag(pic, "VELOCITY")) != nullptr) { mitkIpPicDescriptor *header = mitkIpPicCopyHeader(pic, nullptr); header->data = tsv->value; ConvertHandedness(header); output->SetChannel(header->data, 1); header->data = nullptr; mitkIpPicFree(header); mitkIpPicDelTag(pic, "VELOCITY"); } // Copy the memory to avoid mismatches of malloc() and delete[]. // mitkIpPicGet will always allocate a new memory block with malloc(), // but MITK Images delete the data via delete[]. output->SetImportChannel(pic->data, 0, Image::CopyMemory); pic->data = nullptr; mitkIpPicFree(pic); } diff --git a/Modules/IpPicSupportIO/Internal/mitkPicFileReader.h b/Modules/IpPicSupportIO/Internal/mitkPicFileReader.h index e22f15614f..f472490732 100644 --- a/Modules/IpPicSupportIO/Internal/mitkPicFileReader.h +++ b/Modules/IpPicSupportIO/Internal/mitkPicFileReader.h @@ -1,47 +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 PICFILEREADER_H_HEADER_INCLUDED_C1F48A22 #define PICFILEREADER_H_HEADER_INCLUDED_C1F48A22 #include "mitkAbstractFileReader.h" #include "mitkImage.h" #include "mitkIpPic.h" namespace mitk { //##Documentation //## @brief Reader to read files in DKFZ-pic-format class PicFileReader : public AbstractFileReader { public: PicFileReader(); using AbstractFileReader::Read; - std::vector Read() override; protected: void FillImage(Image::Pointer image); Image::Pointer CreateImage(); + std::vector> DoRead() override; + private: static void ConvertHandedness(mitkIpPicDescriptor *pic); PicFileReader *Clone() const override; }; } // namespace mitk #endif /* PICFILEREADER_H_HEADER_INCLUDED_C1F48A22 */ diff --git a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp index 72b1e39152..8e809e044b 100644 --- a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp +++ b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.cpp @@ -1,315 +1,280 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include #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 structure to change (and reset) the - * local settings in a function scope*/ - struct LocaleSwitch - { - LocaleSwitch(const std::string& newLocale) - : m_OldLocale(std::setlocale(LC_ALL, nullptr)) - , m_NewLocale(newLocale) - { - if (m_OldLocale == nullptr) - { - m_OldLocale = ""; - } - else if (m_NewLocale != m_OldLocale) - { - // set the locale - if (std::setlocale(LC_ALL, m_NewLocale.c_str()) == nullptr) - { - MITK_INFO << "Could not set locale " << m_NewLocale; - m_OldLocale = nullptr; - } - } - } - - ~LocaleSwitch() - { - if (m_OldLocale != nullptr && std::setlocale(LC_ALL, m_OldLocale) == nullptr) - { - MITK_INFO << "Could not reset locale " << m_OldLocale; - } - } - - private: - const char* m_OldLocale; - const std::string m_NewLocale; - }; - /** 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::Read() + 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); 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/autoload/IO/mitkMAPRegistrationWrapperIO.h b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.h index 62f7180a03..cf3042e9ed 100644 --- a/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.h +++ b/Modules/MatchPointRegistration/autoload/IO/mitkMAPRegistrationWrapperIO.h @@ -1,49 +1,51 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef _MITK_MAP_REGISTRATION_WRAPPER_IO_H #define _MITK_MAP_REGISTRATION_WRAPPER_IO_H #include #include "MitkMatchPointRegistrationExports.h" namespace mitk { /** * Offers IO capability for MatchPoint registration wrappers */ class MAPRegistrationWrapperIO : public AbstractFileIO { public: MAPRegistrationWrapperIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - std::vector > Read() override; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; + protected: + std::vector> DoRead() override; + private: MAPRegistrationWrapperIO(const MAPRegistrationWrapperIO& other); MAPRegistrationWrapperIO* IOClone() const override; }; } // end of namespace mitk #endif diff --git a/Modules/ModelFit/cmdapps/CMakeLists.txt b/Modules/ModelFit/cmdapps/CMakeLists.txt index f78ad56dda..cda6e31973 100644 --- a/Modules/ModelFit/cmdapps/CMakeLists.txt +++ b/Modules/ModelFit/cmdapps/CMakeLists.txt @@ -1,34 +1,35 @@ option(BUILD_ModelFitMiniApps "Build commandline tools for the ModelFit module" OFF) if(BUILD_ModelFitMiniApps OR MITK_BUILD_ALL_APPS) # needed include directories include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR} ) # list of miniapps # if an app requires additional dependencies # they are added after a "^^" and separated by "_" set( miniapps GenericFittingMiniApp^^ PixelDumpMiniApp^^ + Fuse3Dto4DImageMiniApp^^ ) foreach(miniapp ${miniapps}) # extract mini app name and dependencies string(REPLACE "^^" "\\;" miniapp_info ${miniapp}) set(miniapp_info_list ${miniapp_info}) list(GET miniapp_info_list 0 appname) list(GET miniapp_info_list 1 raw_dependencies) string(REPLACE "_" "\\;" dependencies "${raw_dependencies}") set(dependencies_list ${dependencies}) mitkFunctionCreateCommandLineApp( NAME ${appname} DEPENDS MitkCore MitkModelFit ${dependencies_list} PACKAGE_DEPENDS ITK ) endforeach() endif(BUILD_ModelFitMiniApps OR MITK_BUILD_ALL_APPS) diff --git a/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp b/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp new file mode 100644 index 0000000000..a2cd40129f --- /dev/null +++ b/Modules/ModelFit/cmdapps/Fuse3Dto4DImageMiniApp.cpp @@ -0,0 +1,167 @@ +/*============================================================================ + +The Medical Imaging Interaction Toolkit (MITK) + +Copyright (c) German Cancer Research Center (DKFZ) +All rights reserved. + +Use of this source code is governed by a 3-clause BSD license that can be +found in the LICENSE file. + +============================================================================*/ + +// std includes +#include +#include + +// itk includes +#include "itksys/SystemTools.hxx" + +// CTK includes +#include "mitkCommandLineParser.h" + +// MITK includes +#include +#include +#include + +mitkCommandLineParser::StringContainerType inFilenames; +std::string outFileName; + +std::vector images; +std::vector timebounds; + +void setupParser(mitkCommandLineParser& parser) +{ + // set general information about your MiniApp + parser.setCategory("Dynamic Data Analysis Tools"); + parser.setTitle("Fuse 3D to 4D Image"); + parser.setDescription("MiniApp that allows to fuse several 3D images (with same geometry) into a 3D+t (4D) image that can be processed as dynamic data."); + 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("Required I/O parameters"); + parser.addArgument( + "inputs", "i", mitkCommandLineParser::StringList, "Input files", "Pathes to the input images that should be fused", us::Any(), false, false, false, mitkCommandLineParser::Input); + parser.addArgument("output", + "o", + mitkCommandLineParser::File, + "Output file path", + "Path to the fused 3D+t image.", + us::Any(), + false, false, false, mitkCommandLineParser::Output); + parser.endGroup(); + + parser.beginGroup("Optional parameters"); + parser.addArgument( + "time", "t", mitkCommandLineParser::StringList, "Time bounds", "Defines the time geometry of the resulting dynamic image in [ms]. The first number is the start time point of the first time step. All other numbers are the max bound of a time step. So the structure is [minBound0 maxBound1 [maxBound2 [... maxBoundN]]]; e.g. \"2 3.5 10\" encodes a time geometry with two time steps and that starts at 2 ms and the second time step starts at 3.5 ms and ends at 10 ms. If not set e propertional time geometry with 1 ms duration will be generated!", us::Any(), true, false, false, mitkCommandLineParser::Input); + 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; + + inFilenames = us::any_cast(parsedArgs["inputs"]); + outFileName = us::any_cast(parsedArgs["output"]); + + if (parsedArgs.count("time")) + { + auto timeBoundsStr = us::any_cast(parsedArgs["time"]); + for (const auto& timeBoundStr : timeBoundsStr) + { + std::istringstream stream; + stream.imbue(std::locale("C")); + stream.str(timeBoundStr); + mitk::TimePointType time = 0 ; + stream >> time; + timebounds.emplace_back(time); + } + } + + return true; +} + +int main(int argc, char* argv[]) +{ + mitkCommandLineParser parser; + setupParser(parser); + + mitk::PreferenceListReaderOptionsFunctor readerFilterFunctor = mitk::PreferenceListReaderOptionsFunctor({ "MITK DICOM Reader v2 (classic config)" }, { "MITK DICOM Reader" }); + + const std::map& parsedArgs = parser.parseArguments(argc, argv); + if (!configureApplicationSettings(parsedArgs)) + { + return EXIT_FAILURE; + }; + + // Show a help message + if (parsedArgs.count("help") || parsedArgs.count("h")) + { + std::cout << parser.helpText(); + return EXIT_SUCCESS; + } + + if (timebounds.empty()) + { + timebounds.resize(inFilenames.size()+1); + std::iota(timebounds.begin(), timebounds.end(), 0.0); + } + else if (inFilenames.size() + 1 != timebounds.size()) + { + std::cerr << "Cannot fuse images. Explicitly specified time bounds do not match. Use --help for more information on how to specify time bounds correctly."; + return EXIT_FAILURE; + }; + + //! [do processing] + try + { + std::cout << "Load images:" << std::endl; + + auto filter = mitk::TemporalJoinImagesFilter::New(); + + unsigned int step = 0; + for (auto path : inFilenames) + { + std::cout << "Time step #"<(path, &readerFilterFunctor); + images.push_back(image); + filter->SetInput(step, image); + ++step; + } + + filter->SetFirstMinTimeBound(timebounds[0]); + filter->SetMaxTimeBounds({ timebounds.begin() + 1, timebounds.end() }); + + std::cout << "Fuse the images ..." << std::endl; + + filter->Update(); + auto output = filter->GetOutput(); + + std::cout << "Save output image: " << outFileName << std::endl; + + mitk::IOUtil::Save(output, outFileName); + + std::cout << "Processing finished." << std::endl; + + return EXIT_SUCCESS; + } + 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/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.cpp index acaa3bc0cb..fb7bc8b6ca 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)); // 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::Read() + 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/DICOMSegIO/mitkDICOMSegmentationIO.h b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h index 5767e9f3fe..123bcb803c 100644 --- a/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h +++ b/Modules/Multilabel/autoload/DICOMSegIO/mitkDICOMSegmentationIO.h @@ -1,62 +1,64 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef __mitkDICOMSegmentationIO_h #define __mitkDICOMSegmentationIO_h #include #include #include namespace mitk { /** * Read and Writes a LabelSetImage to a dcm file * @ingroup Process */ class DICOMSegmentationIO : public mitk::AbstractFileIO { public: typedef mitk::LabelSetImage InputType; typedef itk::Image itkInputImageType; typedef itk::Image itkInternalImageType; DICOMSegmentationIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - /** - * @brief Reads a number of DICOM segmentation from the file system - * @return a vector of mitk::LabelSetImages - * @throws throws an mitk::Exception if an error ocurrs - */ - std::vector Read() override; + ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; + protected: + /** + * @brief Reads a number of DICOM segmentation from the file system + * @return a vector of mitk::LabelSetImages + * @throws throws an mitk::Exception if an error ocurrs + */ + std::vector> DoRead() override; private: DICOMSegmentationIO *IOClone() const override; // -------------- DICOMSegmentationIO specific functions ------------- const std::string CreateMetaDataJsonFile(int layer); void SetLabelProperties(Label *label, dcmqi::SegmentAttributes *segmentAttribute); void AddDICOMTagsToService(); }; } // end of namespace mitk #endif // __mitkDICOMSegmentationIO_h diff --git a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp index 9990ea7032..9466f4a70e 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.cpp @@ -1,636 +1,636 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 // 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"; 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); } 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::Read() + 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); } } } 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 59277a97e5..4b079dc5e4 100644 --- a/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h +++ b/Modules/Multilabel/autoload/IO/mitkLabelSetImageIO.h @@ -1,66 +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 __mitkLabelSetImageIO_h #define __mitkLabelSetImageIO_h #include #include namespace mitk { /** * Writes a LabelSetImage to a file * @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; - /** - * @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 Read() override; + 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/QtWidgets/test/CMakeLists.txt b/Modules/QtWidgets/test/CMakeLists.txt index fe9484dd12..b7ff9b62be 100644 --- a/Modules/QtWidgets/test/CMakeLists.txt +++ b/Modules/QtWidgets/test/CMakeLists.txt @@ -1,9 +1,9 @@ MITK_CREATE_MODULE_TESTS() if(UNIX AND NOT APPLE) - set(qt_platform "-platform minimal") + set(qt_platform -platform minimal) else() unset(qt_platform) endif() mitkAddCustomModuleTest(QmitkAbstractNodeSelectionWidgetTest QmitkAbstractNodeSelectionWidgetTest ${qt_platform}) diff --git a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp index cecd0f20b0..45483cc16a 100644 --- a/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp +++ b/Modules/SegmentationUI/Qmitk/QmitkAdaptiveRegionGrowingToolGUI.cpp @@ -1,956 +1,956 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #include "QmitkAdaptiveRegionGrowingToolGUI.h" #include #include "mitkITKImageImport.h" #include "mitkImageAccessByItk.h" #include "mitkImageTimeSelector.h" #include "mitkNodePredicateDataType.h" #include "mitkProperties.h" #include "mitkTransferFunctionProperty.h" #include "mitkImageStatisticsHolder.h" #include "itkMaskImageFilter.h" #include "itkNumericTraits.h" #include #include #include #include #include "QmitkConfirmSegmentationDialog.h" #include "itkOrImageFilter.h" #include "mitkImageCast.h" #include "mitkImagePixelReadAccessor.h" #include "mitkPixelTypeMultiplex.h" #include "mitkImageCast.h" MITK_TOOL_GUI_MACRO(, QmitkAdaptiveRegionGrowingToolGUI, "") QmitkAdaptiveRegionGrowingToolGUI::QmitkAdaptiveRegionGrowingToolGUI(QWidget *parent) : QmitkToolGUI(), m_DataStorage(nullptr), m_UseVolumeRendering(false), m_UpdateSuggestedThreshold(true), m_SuggestedThValue(0.0) { this->setParent(parent); m_Controls.setupUi(this); m_Controls.m_ThresholdSlider->setDecimals(1); m_Controls.m_ThresholdSlider->setSpinBoxAlignment(Qt::AlignVCenter); m_Controls.m_PreviewSlider->setEnabled(false); m_Controls.m_PreviewSlider->setSingleStep(0.5); // Not yet available // m_Controls.m_PreviewSlider->InvertedAppearance(true); //3D preview doesn't work: T24430. Postponed until reimplementation of segmentation m_Controls.m_cbVolumeRendering->setVisible(false); this->CreateConnections(); this->SetDataNodeNames("labeledRGSegmentation", "RGResult", "RGFeedbackSurface", "maskedSegmentation"); connect(this, SIGNAL(NewToolAssociated(mitk::Tool *)), this, SLOT(OnNewToolAssociated(mitk::Tool *))); } QmitkAdaptiveRegionGrowingToolGUI::~QmitkAdaptiveRegionGrowingToolGUI() { // Removing the observer of the PointSet node if (m_RegionGrow3DTool->GetPointSetNode().IsNotNull()) { m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetAddObserverTag); m_RegionGrow3DTool->GetPointSetNode()->GetData()->RemoveObserver(m_PointSetMoveObserverTag); } this->RemoveHelperNodes(); } void QmitkAdaptiveRegionGrowingToolGUI::OnNewToolAssociated(mitk::Tool *tool) { m_RegionGrow3DTool = dynamic_cast(tool); if (m_RegionGrow3DTool.IsNotNull()) { SetInputImageNode(this->m_RegionGrow3DTool->GetReferenceData()); this->m_DataStorage = this->m_RegionGrow3DTool->GetDataStorage(); this->EnableControls(true); // Watch for point added or modified itk::SimpleMemberCommand::Pointer pointAddedCommand = itk::SimpleMemberCommand::New(); pointAddedCommand->SetCallbackFunction(this, &QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded); m_PointSetAddObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetAddEvent(), pointAddedCommand); m_PointSetMoveObserverTag = m_RegionGrow3DTool->GetPointSetNode()->GetData()->AddObserver(mitk::PointSetMoveEvent(), pointAddedCommand); } else { this->EnableControls(false); } } void QmitkAdaptiveRegionGrowingToolGUI::RemoveHelperNodes() { mitk::DataNode::Pointer imageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (imageNode.IsNotNull()) { m_DataStorage->Remove(imageNode); } mitk::DataNode::Pointer maskedSegmentationNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); if (maskedSegmentationNode.IsNotNull()) { m_DataStorage->Remove(maskedSegmentationNode); } } void QmitkAdaptiveRegionGrowingToolGUI::CreateConnections() { // Connecting GUI components connect((QObject *)(m_Controls.m_pbRunSegmentation), SIGNAL(clicked()), this, SLOT(RunSegmentation())); connect(m_Controls.m_PreviewSlider, SIGNAL(valueChanged(double)), this, SLOT(ChangeLevelWindow(double))); connect((QObject *)(m_Controls.m_pbConfirmSegementation), SIGNAL(clicked()), this, SLOT(ConfirmSegmentation())); connect( m_Controls.m_ThresholdSlider, SIGNAL(maximumValueChanged(double)), this, SLOT(SetUpperThresholdValue(double))); connect( m_Controls.m_ThresholdSlider, SIGNAL(minimumValueChanged(double)), this, SLOT(SetLowerThresholdValue(double))); } void QmitkAdaptiveRegionGrowingToolGUI::SetDataNodeNames(std::string labledSegmentation, std::string binaryImage, std::string surface, std::string maskedSegmentation) { m_NAMEFORLABLEDSEGMENTATIONIMAGE = labledSegmentation; m_NAMEFORBINARYIMAGE = binaryImage; m_NAMEFORSURFACE = surface; m_NAMEFORMASKEDSEGMENTATION = maskedSegmentation; } void QmitkAdaptiveRegionGrowingToolGUI::SetDataStorage(mitk::DataStorage *dataStorage) { m_DataStorage = dataStorage; } void QmitkAdaptiveRegionGrowingToolGUI::SetInputImageNode(mitk::DataNode *node) { m_InputImageNode = node; mitk::Image *inputImage = dynamic_cast(m_InputImageNode->GetData()); if (inputImage) { mitk::ScalarType max = inputImage->GetStatistics()->GetScalarValueMax(); mitk::ScalarType min = inputImage->GetStatistics()->GetScalarValueMin(); m_Controls.m_ThresholdSlider->setMaximum(max); m_Controls.m_ThresholdSlider->setMinimum(min); // Just for initialization m_Controls.m_ThresholdSlider->setMaximumValue(max); m_Controls.m_ThresholdSlider->setMinimumValue(min); } } template static void AccessPixel(mitk::PixelType /*ptype*/, mitk::Image* im, mitk::Point3D p, int& val) { mitk::ImagePixelReadAccessor access(im); val = access.GetPixelByWorldCoordinates(p); } /**Overloaded const verison*/ template -static void AccessPixel(mitk::PixelType /*ptype*/, const mitk::Image* im, mitk::Point3D p, int &val) +static void AccessPixel(mitk::PixelType /*ptype*/, const mitk::Image* im, mitk::Point3D p, int& val) { mitk::ImagePixelReadAccessor access(im); val = access.GetPixelByWorldCoordinates(p); } void QmitkAdaptiveRegionGrowingToolGUI::OnPointAdded() { if (m_RegionGrow3DTool.IsNull()) return; mitk::DataNode *node = m_RegionGrow3DTool->GetPointSetNode(); if (node != nullptr) { mitk::PointSet::Pointer pointSet = dynamic_cast(node->GetData()); if (pointSet.IsNull()) { QMessageBox::critical(nullptr, "QmitkAdaptiveRegionGrowingToolGUI", "PointSetNode does not contain a pointset"); return; } m_Controls.m_lblSetSeedpoint->setText(""); const mitk::Image *image = dynamic_cast(m_InputImageNode->GetData()); mitk::Point3D seedPoint = pointSet ->GetPointSet( mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget0"))->GetTimeStep()) ->GetPoints() ->ElementAt(0); if (image->GetGeometry()->IsInside(seedPoint)) mitkPixelTypeMultiplex3( AccessPixel, image->GetChannelDescriptor().GetPixelType(), image, seedPoint, m_SeedpointValue) else return; /* In this case the seedpoint is placed e.g. in the lung or bronchialtree * The lowerFactor sets the windowsize depending on the regiongrowing direction */ m_CurrentRGDirectionIsUpwards = true; if (m_SeedpointValue < -500) { m_CurrentRGDirectionIsUpwards = false; } // Initializing the region by the area around the seedpoint m_SeedPointValueMean = 0; itk::Index<3> currentIndex, runningIndex; mitk::ScalarType pixelValues[125]; unsigned int pos(0); image->GetGeometry(0)->WorldToIndex(seedPoint, currentIndex); runningIndex = currentIndex; for (int i = runningIndex[0] - 2; i <= runningIndex[0] + 2; i++) { for (int j = runningIndex[1] - 2; j <= runningIndex[1] + 2; j++) { for (int k = runningIndex[2] - 2; k <= runningIndex[2] + 2; k++) { currentIndex[0] = i; currentIndex[1] = j; currentIndex[2] = k; if (image->GetGeometry()->IsIndexInside(currentIndex)) { int component = 0; m_InputImageNode->GetIntProperty("Image.Displayed Component", component); mitkPixelTypeMultiplex4(mitk::FastSinglePixelAccess, image->GetChannelDescriptor().GetPixelType(), image, nullptr, currentIndex, pixelValues[pos]); pos++; } else { pixelValues[pos] = std::numeric_limits::min(); pos++; } } } } // Now calculation mean of the pixelValues // Now calculation mean of the pixelValues unsigned int numberOfValues(0); for (auto &pixelValue : pixelValues) { if (pixelValue > std::numeric_limits::min()) { m_SeedPointValueMean += pixelValue; numberOfValues++; } } m_SeedPointValueMean = m_SeedPointValueMean / numberOfValues; mitk::ScalarType var = 0; if (numberOfValues > 1) { for (auto &pixelValue : pixelValues) { if (pixelValue > std::numeric_limits::min()) { var += (pixelValue - m_SeedPointValueMean) * (pixelValue - m_SeedPointValueMean); } } var /= numberOfValues - 1; } mitk::ScalarType stdDev = sqrt(var); /* * Here the upper- and lower threshold is calculated: * The windowSize is 20% of the maximum range of the intensity values existing in the current image * If the RG direction is upwards the lower TH is meanSeedValue-0.15*windowSize and upper TH is * meanSeedValue+0.85*windowsSize * if the RG direction is downwards the lower TH is meanSeedValue-0.85*windowSize and upper TH is * meanSeedValue+0.15*windowsSize */ mitk::ScalarType min = image->GetStatistics()->GetScalarValueMin(); mitk::ScalarType max = image->GetStatistics()->GetScalarValueMax(); mitk::ScalarType windowSize = max - min; windowSize = 0.15 * windowSize; if (m_CurrentRGDirectionIsUpwards) { m_LOWERTHRESHOLD = m_SeedPointValueMean - stdDev; m_UPPERTHRESHOLD = m_SeedpointValue + windowSize; if (m_UPPERTHRESHOLD > max) m_UPPERTHRESHOLD = max; m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); } else { m_UPPERTHRESHOLD = m_SeedPointValueMean; if (m_SeedpointValue > m_SeedPointValueMean) m_UPPERTHRESHOLD = m_SeedpointValue; m_LOWERTHRESHOLD = m_SeedpointValue - windowSize; if (m_LOWERTHRESHOLD < min) m_LOWERTHRESHOLD = min; m_Controls.m_ThresholdSlider->setMinimumValue(m_LOWERTHRESHOLD); m_Controls.m_ThresholdSlider->setMaximumValue(m_UPPERTHRESHOLD); } } } void QmitkAdaptiveRegionGrowingToolGUI::RunSegmentation() { if (m_InputImageNode.IsNull()) { QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please specify the image in Datamanager!"); return; } mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { QMessageBox::information(nullptr, "Adaptive Region Growing functionality", "Please insert a seed point inside the " "image.\n\nFirst press the \"Define Seed " "Point\" button,\nthen click left mouse " "button inside the image."); return; } // safety if no pointSet or pointSet empty mitk::PointSet::Pointer seedPointSet = dynamic_cast(node->GetData()); if (seedPointSet.IsNull()) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } int timeStep = mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget0"))->GetTimeStep(); if (!(seedPointSet->GetSize(timeStep))) { m_Controls.m_pbRunSegmentation->setEnabled(true); QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "The seed point is empty! Please choose a new seed point."); return; } QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); mitk::PointSet::PointType seedPoint = seedPointSet->GetPointSet(timeStep)->GetPoints()->Begin().Value(); mitk::Image::Pointer orgImage = dynamic_cast(m_InputImageNode->GetData()); if (orgImage.IsNotNull()) { if (orgImage->GetDimension() == 4) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(orgImage); timeSelector->SetTimeNr(timeStep); timeSelector->UpdateLargestPossibleRegion(); mitk::Image *timedImage = timeSelector->GetOutput(); AccessByItk_2(timedImage, StartRegionGrowing, timedImage->GetGeometry(), seedPoint); } else if (orgImage->GetDimension() == 3) { // QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); //set the cursor to waiting AccessByItk_2(orgImage, StartRegionGrowing, orgImage->GetGeometry(), seedPoint); // QApplication::restoreOverrideCursor();//reset cursor } else { QApplication::restoreOverrideCursor(); // reset cursor QMessageBox::information( nullptr, "Adaptive Region Growing functionality", "Only images of dimension 3 or 4 can be processed!"); return; } } EnableControls(true); // Segmentation ran successfully, so enable all controls. node->SetVisibility(true); QApplication::restoreOverrideCursor(); // reset cursor } template void QmitkAdaptiveRegionGrowingToolGUI::StartRegionGrowing(itk::Image *itkImage, mitk::BaseGeometry *imageGeometry, mitk::PointSet::PointType seedPoint) { typedef itk::Image InputImageType; typedef typename InputImageType::IndexType IndexType; typedef itk::ConnectedAdaptiveThresholdImageFilter RegionGrowingFilterType; typename RegionGrowingFilterType::Pointer regionGrower = RegionGrowingFilterType::New(); typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::MaskImageFilter MaskImageFilterType; if (!imageGeometry->IsInside(seedPoint)) { QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information(nullptr, "Segmentation functionality", "The seed point is outside of the image! Please choose a position inside the image!"); return; } IndexType seedIndex; imageGeometry->WorldToIndex(seedPoint, seedIndex); // convert world coordinates to image indices if (m_SeedpointValue > m_UPPERTHRESHOLD || m_SeedpointValue < m_LOWERTHRESHOLD) { QApplication::restoreOverrideCursor(); // reset cursor to be able to click ok with the regular mouse cursor QMessageBox::information( nullptr, "Segmentation functionality", "The seed point is outside the defined thresholds! Please set a new seed point or adjust the thresholds."); MITK_INFO << "Mean: " << m_SeedPointValueMean; return; } // Setting the direction of the regiongrowing. For dark structures e.g. the lung the regiongrowing // is performed starting at the upper value going to the lower one regionGrower->SetGrowingDirectionIsUpwards(m_CurrentRGDirectionIsUpwards); regionGrower->SetInput(itkImage); regionGrower->AddSeed(seedIndex); // In some cases we have to subtract 1 for the lower threshold and add 1 to the upper. // Otherwise no region growing is done. Maybe a bug in the ConnectiveAdaptiveThresholdFilter regionGrower->SetLower(m_LOWERTHRESHOLD - 1); regionGrower->SetUpper(m_UPPERTHRESHOLD + 1); try { regionGrower->Update(); } catch (itk::ExceptionObject &exc) { QMessageBox errorInfo; errorInfo.setWindowTitle("Adaptive RG Segmentation Functionality"); errorInfo.setIcon(QMessageBox::Critical); errorInfo.setText("An error occurred during region growing!"); errorInfo.setDetailedText(exc.what()); errorInfo.exec(); return; // can't work } catch (...) { QMessageBox::critical(nullptr, "Adaptive RG Segmentation Functionality", "An error occurred during region growing!"); return; } mitk::Image::Pointer resultImage = mitk::ImportItkImage(regionGrower->GetOutput())->Clone(); // initialize slider m_Controls.m_PreviewSlider->setMinimum(m_LOWERTHRESHOLD); mitk::ScalarType max = m_SeedpointValue + resultImage->GetStatistics()->GetScalarValueMax(); if (max < m_UPPERTHRESHOLD) m_Controls.m_PreviewSlider->setMaximum(max); else m_Controls.m_PreviewSlider->setMaximum(m_UPPERTHRESHOLD); this->m_DetectedLeakagePoint = regionGrower->GetLeakagePoint(); if (m_CurrentRGDirectionIsUpwards) { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean - 1); } else { m_Controls.m_PreviewSlider->setValue(m_SeedPointValueMean + 1); } this->m_SliderInitialized = true; // create new node and then delete the old one if there is one mitk::DataNode::Pointer newNode = mitk::DataNode::New(); newNode->SetData(resultImage); // set some properties newNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORLABLEDSEGMENTATIONIMAGE)); newNode->SetProperty("helper object", mitk::BoolProperty::New(true)); newNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); newNode->SetProperty("layer", mitk::IntProperty::New(1)); newNode->SetProperty("opacity", mitk::FloatProperty::New(0.7)); // delete the old image, if there was one: mitk::DataNode::Pointer binaryNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); m_DataStorage->Remove(binaryNode); // now add result to data tree m_DataStorage->Add(newNode, m_InputImageNode); typename InputImageType::Pointer inputImageItk; mitk::CastToItkImage(resultImage, inputImageItk); // volume rendering preview masking typename ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); thresholdFilter->SetInput(inputImageItk); thresholdFilter->SetInsideValue(1); thresholdFilter->SetOutsideValue(0); double sliderVal = this->m_Controls.m_PreviewSlider->value(); if (m_CurrentRGDirectionIsUpwards) { thresholdFilter->SetLowerThreshold(sliderVal); thresholdFilter->SetUpperThreshold(itk::NumericTraits::max()); } else { thresholdFilter->SetLowerThreshold(itk::NumericTraits::min()); thresholdFilter->SetUpperThreshold(sliderVal); } thresholdFilter->SetInPlace(false); typename MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); maskFilter->SetInput(inputImageItk); maskFilter->SetInPlace(false); maskFilter->SetMaskImage(thresholdFilter->GetOutput()); maskFilter->SetOutsideValue(0); maskFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkMask; mitk::CastToMitkImage(maskFilter->GetOutput(), mitkMask); mitk::DataNode::Pointer maskedNode = mitk::DataNode::New(); maskedNode->SetData(mitkMask); // set some properties maskedNode->SetProperty("name", mitk::StringProperty::New(m_NAMEFORMASKEDSEGMENTATION)); maskedNode->SetProperty("helper object", mitk::BoolProperty::New(true)); maskedNode->SetProperty("color", mitk::ColorProperty::New(0.0, 1.0, 0.0)); maskedNode->SetProperty("layer", mitk::IntProperty::New(1)); maskedNode->SetProperty("opacity", mitk::FloatProperty::New(0.0)); // delete the old image, if there was one: mitk::DataNode::Pointer deprecatedMask = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); m_DataStorage->Remove(deprecatedMask); // now add result to data tree m_DataStorage->Add(maskedNode, m_InputImageNode); this->InitializeLevelWindow(); if (m_UseVolumeRendering) this->EnableVolumeRendering(true); m_UpdateSuggestedThreshold = true; // reset first stored threshold value // Setting progress to finished mitk::ProgressBar::GetInstance()->Progress(357); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::InitializeLevelWindow() { // get the preview from the datatree mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); mitk::ScalarType *level = new mitk::ScalarType(0.0); mitk::ScalarType *window = new mitk::ScalarType(1.0); int upper; if (m_CurrentRGDirectionIsUpwards) { upper = m_UPPERTHRESHOLD - m_SeedpointValue; } else { upper = m_SeedpointValue - m_LOWERTHRESHOLD; } tempLevelWindow.SetRangeMinMax(mitk::ScalarType(0), mitk::ScalarType(upper)); // get the suggested threshold from the detected leakage-point and adjust the slider if (m_CurrentRGDirectionIsUpwards) { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = m_UPPERTHRESHOLD - (m_SeedpointValue) + 0.5; } else { this->m_Controls.m_PreviewSlider->setValue(m_SeedpointValue); *level = (m_SeedpointValue)-m_LOWERTHRESHOLD + 0.5; } tempLevelWindow.SetLevelWindow(*level, *window); newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); // update the widgets mitk::RenderingManager::GetInstance()->RequestUpdateAll(); m_SliderInitialized = true; // inquiry need to fix bug#1828 static int lastSliderPosition = 0; if ((this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1) == lastSliderPosition) { this->ChangeLevelWindow(lastSliderPosition); } lastSliderPosition = this->m_SeedpointValue + this->m_DetectedLeakagePoint - 1; if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int)(*level + 0.5)); // lower threshold for labeled image } void QmitkAdaptiveRegionGrowingToolGUI::ChangeLevelWindow(double newValue) { if (m_SliderInitialized) { // do nothing, if no preview exists mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; mitk::LevelWindow tempLevelWindow; newNode->GetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); // get the levelWindow associated with the preview mitk::ScalarType level; // = this->m_UPPERTHRESHOLD - newValue + 0.5; mitk::ScalarType *window = new mitk::ScalarType(1); // adjust the levelwindow according to the position of the slider (newvalue) if (m_CurrentRGDirectionIsUpwards) { level = m_UPPERTHRESHOLD - newValue + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } else { level = newValue - m_LOWERTHRESHOLD + 0.5; tempLevelWindow.SetLevelWindow(level, *window); } newNode->SetLevelWindow(tempLevelWindow, nullptr, "levelwindow"); if (m_UseVolumeRendering) this->UpdateVolumeRenderingThreshold((int)(level - 0.5)); // lower threshold for labeled image newNode->SetVisibility(true); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::DecreaseSlider() { // moves the slider one step to the left, when the "-"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->minimum()) { int newValue = this->m_Controls.m_PreviewSlider->value() - 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::IncreaseSlider() { // moves the slider one step to the right, when the "+"-button is pressed if (this->m_Controls.m_PreviewSlider->value() != this->m_Controls.m_PreviewSlider->maximum()) { int newValue = this->m_Controls.m_PreviewSlider->value() + 1; this->ChangeLevelWindow(newValue); this->m_Controls.m_PreviewSlider->setValue(newValue); } } void QmitkAdaptiveRegionGrowingToolGUI::ConfirmSegmentation() { // get image node if (m_InputImageNode.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "Please specify the image in Datamanager!"); return; } // get image data mitk::Image::Pointer orgImage = dynamic_cast(m_InputImageNode->GetData()); if (orgImage.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Image found!"); return; } // get labeled segmentation mitk::Image::Pointer labeledSeg = (mitk::Image *)m_DataStorage->GetNamedObject(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (labeledSeg.IsNull()) { QMessageBox::critical(nullptr, "Adaptive region growing functionality", "No Segmentation Preview found!"); return; } mitk::DataNode::Pointer newNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (newNode.IsNull()) return; QmitkConfirmSegmentationDialog dialog; QString segName = QString::fromStdString(m_RegionGrow3DTool->GetCurrentSegmentationName()); dialog.SetSegmentationName(segName); int result = dialog.exec(); switch (result) { case QmitkConfirmSegmentationDialog::CREATE_NEW_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(false); break; case QmitkConfirmSegmentationDialog::OVERWRITE_SEGMENTATION: m_RegionGrow3DTool->SetOverwriteExistingSegmentation(true); break; case QmitkConfirmSegmentationDialog::CANCEL_SEGMENTATION: return; } mitk::Image::Pointer img = dynamic_cast(newNode->GetData()); AccessByItk(img, ITKThresholding); // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); newNode->SetVisibility(false); m_Controls.m_cbVolumeRendering->setChecked(false); // TODO disable slider etc... if (m_RegionGrow3DTool.IsNotNull()) { m_RegionGrow3DTool->ConfirmSegmentation(); } } template void QmitkAdaptiveRegionGrowingToolGUI::ITKThresholding(itk::Image *itkImage) { mitk::Image::Pointer originalSegmentation = dynamic_cast(this->m_RegionGrow3DTool->GetTargetSegmentationNode()->GetData()); int timeStep = mitk::BaseRenderer::GetInstance(mitk::BaseRenderer::GetRenderWindowByName("stdmulti.widget0"))->GetTimeStep(); if (originalSegmentation) { typedef itk::Image InputImageType; typedef itk::Image SegmentationType; // select single 3D volume if we have more than one time step typename SegmentationType::Pointer originalSegmentationInITK = SegmentationType::New(); if (originalSegmentation->GetTimeGeometry()->CountTimeSteps() > 1) { mitk::ImageTimeSelector::Pointer timeSelector = mitk::ImageTimeSelector::New(); timeSelector->SetInput(originalSegmentation); timeSelector->SetTimeNr(timeStep); timeSelector->UpdateLargestPossibleRegion(); CastToItkImage(timeSelector->GetOutput(), originalSegmentationInITK); } else // use original { CastToItkImage(originalSegmentation, originalSegmentationInITK); } // Fill current preiview image in segmentation image originalSegmentationInITK->FillBuffer(0); itk::ImageRegionIterator itOutput(originalSegmentationInITK, originalSegmentationInITK->GetLargestPossibleRegion()); itk::ImageRegionIterator itInput(itkImage, itkImage->GetLargestPossibleRegion()); itOutput.GoToBegin(); itInput.GoToBegin(); // calculate threhold from slider value int currentTreshold = 0; if (m_CurrentRGDirectionIsUpwards) { currentTreshold = m_UPPERTHRESHOLD - m_Controls.m_PreviewSlider->value() + 1; } else { currentTreshold = m_Controls.m_PreviewSlider->value() - m_LOWERTHRESHOLD; } // iterate over image and set pixel in segmentation according to thresholded labeled image while (!itOutput.IsAtEnd() && !itInput.IsAtEnd()) { // Use threshold slider to determine if pixel is set to 1 if (itInput.Value() != 0 && itInput.Value() >= static_cast::PixelType>(currentTreshold)) { itOutput.Set(1); } ++itOutput; ++itInput; } // combine current working segmentation image with our region growing result originalSegmentation->SetVolume((void *)(originalSegmentationInITK->GetPixelContainer()->GetBufferPointer()), timeStep); originalSegmentation->Modified(); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } } void QmitkAdaptiveRegionGrowingToolGUI::EnableControls(bool enable) { if (m_RegionGrow3DTool.IsNull()) return; // Check if seed point is already set, if not leave RunSegmentation disabled // if even m_DataStorage is nullptr leave node nullptr mitk::DataNode::Pointer node = m_RegionGrow3DTool->GetPointSetNode(); if (node.IsNull()) { this->m_Controls.m_pbRunSegmentation->setEnabled(false); } else { this->m_Controls.m_pbRunSegmentation->setEnabled(enable); } // Check if a segmentation exists, if not leave segmentation dependent disabled. // if even m_DataStorage is nullptr leave node nullptr node = m_DataStorage ? m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE) : nullptr; if (node.IsNull()) { this->m_Controls.m_PreviewSlider->setEnabled(false); this->m_Controls.m_pbConfirmSegementation->setEnabled(false); } else { this->m_Controls.m_PreviewSlider->setEnabled(enable); this->m_Controls.m_pbConfirmSegementation->setEnabled(enable); } this->m_Controls.m_cbVolumeRendering->setEnabled(enable); } void QmitkAdaptiveRegionGrowingToolGUI::EnableVolumeRendering(bool enable) { mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); if (node.IsNull()) return; if (enable) { node->SetBoolProperty("volumerendering", enable); node->SetBoolProperty("volumerendering.uselod", true); } else { node->SetBoolProperty("volumerendering", enable); } double val = this->m_Controls.m_PreviewSlider->value(); this->ChangeLevelWindow(val); mitk::RenderingManager::GetInstance()->RequestUpdateAll(); } void QmitkAdaptiveRegionGrowingToolGUI::UpdateVolumeRenderingThreshold(int) { typedef short PixelType; typedef itk::Image InputImageType; typedef itk::BinaryThresholdImageFilter ThresholdFilterType; typedef itk::MaskImageFilter MaskImageFilterType; mitk::DataNode::Pointer grownImageNode = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); mitk::Image::Pointer grownImage = dynamic_cast(grownImageNode->GetData()); if (!grownImage) { MITK_ERROR << "Missing data node for labeled segmentation image."; return; } InputImageType::Pointer itkGrownImage; mitk::CastToItkImage(grownImage, itkGrownImage); ThresholdFilterType::Pointer thresholdFilter = ThresholdFilterType::New(); thresholdFilter->SetInput(itkGrownImage); thresholdFilter->SetInPlace(false); double sliderVal = this->m_Controls.m_PreviewSlider->value(); PixelType threshold = itk::NumericTraits::min(); if (m_CurrentRGDirectionIsUpwards) { threshold = static_cast(m_UPPERTHRESHOLD - sliderVal + 0.5); thresholdFilter->SetLowerThreshold(threshold); thresholdFilter->SetUpperThreshold(itk::NumericTraits::max()); } else { threshold = sliderVal - m_LOWERTHRESHOLD + 0.5; thresholdFilter->SetLowerThreshold(itk::NumericTraits::min()); thresholdFilter->SetUpperThreshold(threshold); } thresholdFilter->UpdateLargestPossibleRegion(); MaskImageFilterType::Pointer maskFilter = MaskImageFilterType::New(); maskFilter->SetInput(itkGrownImage); maskFilter->SetInPlace(false); maskFilter->SetMaskImage(thresholdFilter->GetOutput()); maskFilter->SetOutsideValue(0); maskFilter->UpdateLargestPossibleRegion(); mitk::Image::Pointer mitkMaskedImage; mitk::CastToMitkImage(maskFilter->GetOutput(), mitkMaskedImage); mitk::DataNode::Pointer maskNode = m_DataStorage->GetNamedNode(m_NAMEFORMASKEDSEGMENTATION); maskNode->SetData(mitkMaskedImage); } void QmitkAdaptiveRegionGrowingToolGUI::UseVolumeRendering(bool on) { m_UseVolumeRendering = on; this->EnableVolumeRendering(on); } void QmitkAdaptiveRegionGrowingToolGUI::SetLowerThresholdValue(double lowerThreshold) { m_LOWERTHRESHOLD = lowerThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::SetUpperThresholdValue(double upperThreshold) { m_UPPERTHRESHOLD = upperThreshold; } void QmitkAdaptiveRegionGrowingToolGUI::Deactivated() { // make the segmentation preview node invisible mitk::DataNode::Pointer node = m_DataStorage->GetNamedNode(m_NAMEFORLABLEDSEGMENTATIONIMAGE); if (node.IsNotNull()) { node->SetVisibility(false); } // disable volume rendering preview after the segmentation node was created this->EnableVolumeRendering(false); m_Controls.m_cbVolumeRendering->setChecked(false); } void QmitkAdaptiveRegionGrowingToolGUI::Activated() { } diff --git a/Modules/TubeGraph/include/mitkTubeGraphIO.h b/Modules/TubeGraph/include/mitkTubeGraphIO.h index 0124cbcd3b..b4a0b0afea 100644 --- a/Modules/TubeGraph/include/mitkTubeGraphIO.h +++ b/Modules/TubeGraph/include/mitkTubeGraphIO.h @@ -1,69 +1,71 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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_TUBE_GRAPH_IO_H_ #define _MITK_TUBE_GRAPH_IO_H_ #include #include #include #include "mitkTubeGraph.h" class TiXmlElement; namespace mitk { /** * @brief reader and writer for xml representations of mitk::TubeGraph * * @ingroup IO */ class TubeGraphIO : public mitk::AbstractFileIO { public: TubeGraphIO(); // -------------- AbstractFileReader ------------- using AbstractFileReader::Read; - std::vector Read() override; ConfidenceLevel GetReaderConfidenceLevel() const override; // -------------- AbstractFileWriter ------------- void Write() override; ConfidenceLevel GetWriterConfidenceLevel() const override; static CustomMimeType TUBEGRAPH_MIMETYPE() // tsf { CustomMimeType mimeType(TUBEGRAPH_MIMETYPE_NAME()); mimeType.AddExtension("tsf"); mimeType.SetCategory("Graphs"); mimeType.SetComment("MITK Tube Graph Structure File"); return mimeType; } static std::string TUBEGRAPH_MIMETYPE_NAME() { static std::string name = mitk::IOMimeTypes::DEFAULT_BASE_NAME() + ".graphs.tubular-sructure"; return name; } + protected: + std::vector> DoRead() override; + private: TubeGraphIO *IOClone() const override; TubeGraphIO(const TubeGraphIO &other); const BoundingBox::Pointer ComputeBoundingBox(TubeGraph::Pointer graph) const; }; } #endif //_MITK_SURFACE_VTK_IO_H_ diff --git a/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp b/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp index c313192ff3..9d764365d5 100644 --- a/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp +++ b/Modules/TubeGraph/src/IO/mitkTubeGraphIO.cpp @@ -1,593 +1,593 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) 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 "mitkTubeGraphIO.h" #include "mitkCircularProfileTubeElement.h" #include "mitkTubeGraphDefinitions.h" #include "mitkTubeGraphProperty.h" #include #include #include #include namespace mitk { TubeGraphIO::TubeGraphIO(const TubeGraphIO &other) : AbstractFileIO(other) {} TubeGraphIO::TubeGraphIO() : AbstractFileIO( mitk::TubeGraph::GetStaticNameOfClass(), mitk::TubeGraphIO::TUBEGRAPH_MIMETYPE(), "Tube Graph Structure File") { this->RegisterService(); } - std::vector TubeGraphIO::Read() + std::vector TubeGraphIO::DoRead() { std::locale::global(std::locale("C")); std::vector> result; InputStream stream(this); TiXmlDocument doc; stream >> doc; if (!doc.Error()) { TubeGraph::Pointer newTubeGraph = TubeGraph::New(); TiXmlHandle hDoc(&doc); TiXmlElement *pElem; TiXmlHandle hRoot(nullptr); pElem = hDoc.FirstChildElement().Element(); // save this for later hRoot = TiXmlHandle(pElem); pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_GEOMETRY).Element(); // read geometry mitk::Geometry3D::Pointer geometry = mitk::Geometry3D::New(); geometry->Initialize(); // read origin mitk::Point3D origin; double temp = 0; pElem->Attribute(mitk::TubeGraphDefinitions::XML_ORIGIN_X, &temp); origin[0] = temp; pElem->Attribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Y, &temp); origin[1] = temp; pElem->Attribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Z, &temp); origin[2] = temp; geometry->SetOrigin(origin); // read spacing Vector3D spacing; pElem->Attribute(mitk::TubeGraphDefinitions::XML_SPACING_X, &temp); spacing.SetElement(0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_SPACING_Y, &temp); spacing.SetElement(1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_SPACING_Z, &temp); spacing.SetElement(2, temp); geometry->SetSpacing(spacing); // read transform vtkMatrix4x4 *m = vtkMatrix4x4::New(); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_XX, &temp); m->SetElement(0, 0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_XY, &temp); m->SetElement(1, 0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_XZ, &temp); m->SetElement(2, 0, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_YX, &temp); m->SetElement(0, 1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_YY, &temp); m->SetElement(1, 1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_YZ, &temp); m->SetElement(2, 1, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZX, &temp); m->SetElement(0, 2, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZY, &temp); m->SetElement(1, 2, temp); pElem->Attribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZZ, &temp); m->SetElement(2, 2, temp); m->SetElement(0, 3, origin[0]); m->SetElement(1, 3, origin[1]); m->SetElement(2, 3, origin[2]); m->SetElement(3, 3, 1); geometry->SetIndexToWorldTransformByVtkMatrix(m); geometry->SetImageGeometry(false); // read tube graph // read vertices pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_VERTICES).Element(); if (pElem != nullptr) { // walk through the vertices for (TiXmlElement *vertexElement = pElem->FirstChildElement(); vertexElement != nullptr; vertexElement = vertexElement->NextSiblingElement()) { int vertexID(0); mitk::Point3D coordinate; coordinate.Fill(0.0); double diameter(0); vertexElement->Attribute(mitk::TubeGraphDefinitions::XML_VERTEX_ID, &vertexID); TiXmlElement *tubeElement = vertexElement->FirstChildElement(); tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, &temp); coordinate[0] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, &temp); coordinate[1] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, &temp); coordinate[2] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, &diameter); mitk::TubeGraphVertex vertexData; auto *newElement = new mitk::CircularProfileTubeElement(coordinate, diameter); vertexData.SetTubeElement(newElement); mitk::TubeGraph::VertexDescriptorType newVertex = newTubeGraph->AddVertex(vertexData); if (static_cast(newVertex) != vertexID) { MITK_ERROR << "Aborting tube graph creation, different vertex ids."; return result; ; } } } // read edges pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_EDGES).Element(); if (pElem != nullptr) { // walk through the edges auto edgeElement = pElem->FirstChildElement(); for ( ; edgeElement != nullptr; edgeElement = edgeElement->NextSiblingElement()) { int edgeID(0), edgeSourceID(0), edgeTargetID(0); mitk::Point3D coordinate; double diameter(0); edgeElement->Attribute(mitk::TubeGraphDefinitions::XML_EDGE_ID, &edgeID); edgeElement->Attribute(mitk::TubeGraphDefinitions::XML_EDGE_SOURCE_ID, &edgeSourceID); edgeElement->Attribute(mitk::TubeGraphDefinitions::XML_EDGE_TARGET_ID, &edgeTargetID); mitk::TubeGraphEdge edgeData; for (TiXmlElement *tubeElement = edgeElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_ELEMENT); tubeElement != nullptr; tubeElement = tubeElement->NextSiblingElement()) { tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, &temp); coordinate[0] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, &temp); coordinate[1] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, &temp); coordinate[2] = temp; tubeElement->Attribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, &diameter); auto *newElement = new mitk::CircularProfileTubeElement(coordinate, diameter); edgeData.AddTubeElement(newElement); } try { newTubeGraph->AddEdge(edgeSourceID, edgeTargetID, edgeData); } catch (const std::runtime_error &error) { MITK_ERROR << error.what(); return result; } } } // Compute bounding box BoundingBox::Pointer bb = this->ComputeBoundingBox(newTubeGraph); geometry->SetBounds(bb->GetBounds()); MITK_INFO << "Tube Graph read"; MITK_INFO << "Edge numb:" << newTubeGraph->GetNumberOfEdges() << " Vertices: " << newTubeGraph->GetNumberOfVertices(); MITK_INFO << "Reading tube graph property"; mitk::TubeGraphProperty::Pointer newProperty = mitk::TubeGraphProperty::New(); // read label groups pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_LABELGROUPS).Element(); if (pElem != nullptr) { // walk through the label groups for (TiXmlElement *labelGroupElement = pElem->FirstChildElement(); labelGroupElement != nullptr; labelGroupElement = labelGroupElement->NextSiblingElement()) { auto *newLabelGroup = new mitk::TubeGraphProperty::LabelGroup(); const char *labelGroupName; labelGroupName = labelGroupElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str()); if (labelGroupName) newLabelGroup->labelGroupName = labelGroupName; for (TiXmlElement *labelElement = labelGroupElement->FirstChildElement(mitk::TubeGraphDefinitions::XML_LABEL); labelElement != nullptr; labelElement = labelElement->NextSiblingElement()) { auto *newLabel = new mitk::TubeGraphProperty::LabelGroup::Label(); const char *labelName; bool isVisible = true; Color color; labelName = labelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str()); if (labelName) newLabel->labelName = labelName; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_VISIBILITY, &temp); if (temp == 0) isVisible = false; else isVisible = true; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_R, &temp); color[0] = temp; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_G, &temp); color[1] = temp; labelElement->Attribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_B, &temp); color[2] = temp; newLabel->isVisible = isVisible; newLabel->labelColor = color; newLabelGroup->labels.push_back(newLabel); } newProperty->AddLabelGroup(newLabelGroup, newProperty->GetLabelGroups().size()); } } // read attributations pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTIONS).Element(); if (pElem != nullptr) { std::map tubeToLabelsMap; for (TiXmlElement *tubeToLabelElement = pElem->FirstChildElement(); tubeToLabelElement != nullptr; tubeToLabelElement = tubeToLabelElement->NextSiblingElement()) { TubeGraph::TubeDescriptorType tube; auto *labelGroup = new mitk::TubeGraphProperty::LabelGroup(); auto *label = new mitk::TubeGraphProperty::LabelGroup::Label(); tubeToLabelElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, &temp); tube.first = temp; tubeToLabelElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, &temp); tube.second = temp; const char *labelGroupName = tubeToLabelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME.c_str()); if (labelGroupName) labelGroup = newProperty->GetLabelGroupByName(labelGroupName); const char *labelName = tubeToLabelElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_LABEL_NAME.c_str()); if (labelName) label = newProperty->GetLabelByName(labelGroup, labelName); if (tube != TubeGraph::ErrorId && labelGroup != nullptr && label != nullptr) { TubeGraphProperty::TubeToLabelGroupType tubeToLabelGroup(tube, labelGroupName); tubeToLabelsMap.insert( std::pair(tubeToLabelGroup, labelName)); } } if (tubeToLabelsMap.size() > 0) newProperty->SetTubesToLabels(tubeToLabelsMap); } // read annotations pElem = hRoot.FirstChildElement(mitk::TubeGraphDefinitions::XML_ANNOTATIONS).Element(); if (pElem != nullptr) { for (TiXmlElement *annotationElement = pElem->FirstChildElement(); annotationElement != nullptr; annotationElement = annotationElement->NextSiblingElement()) { auto *annotation = new mitk::TubeGraphProperty::Annotation(); std::string annotationName; std::string annotationDescription; TubeGraph::TubeDescriptorType tube; annotationName = annotationElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_ANNOTATION_NAME.c_str()); annotation->name = annotationName; annotationDescription = annotationElement->Attribute((char *)mitk::TubeGraphDefinitions::XML_ANNOTATION_DESCRIPTION.c_str()); annotation->description = annotationDescription; annotationElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, &temp); tube.first = temp; annotationElement->Attribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, &temp); tube.second = temp; if (tube != TubeGraph::ErrorId) { annotation->tube = tube; newProperty->AddAnnotation(annotation); } } } MITK_INFO << "Tube Graph Property read"; newTubeGraph->SetGeometry(geometry); newTubeGraph->SetProperty("Tube Graph.Visualization Information", newProperty); result.push_back(newTubeGraph.GetPointer()); } else { mitkThrow() << "Parsing error at line " << doc.ErrorRow() << ", col " << doc.ErrorCol() << ": " << doc.ErrorDesc(); } return result; } AbstractFileIO::ConfidenceLevel TubeGraphIO::GetReaderConfidenceLevel() const { if (AbstractFileIO::GetReaderConfidenceLevel() == Unsupported) return Unsupported; return Supported; } void TubeGraphIO::Write() { OutputStream out(this); if (!out.good()) { mitkThrow() << "Stream not good."; } std::locale previousLocale(out.getloc()); std::locale I("C"); out.imbue(I); const auto *tubeGraph = dynamic_cast(this->GetInput()); // Get geometry of the tube graph mitk::Geometry3D::Pointer geometry = dynamic_cast(tubeGraph->GetGeometry()); // Get property of the tube graph mitk::TubeGraphProperty::Pointer tubeGraphProperty = dynamic_cast( tubeGraph->GetProperty("Tube Graph.Visualization Information").GetPointer()); // Create XML document TiXmlDocument documentXML; { // Begin document auto *declXML = new TiXmlDeclaration("1.0", "", ""); documentXML.LinkEndChild(declXML); auto *mainXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_TUBEGRAPH_FILE); mainXML->SetAttribute(mitk::TubeGraphDefinitions::XML_FILE_VERSION, mitk::TubeGraphDefinitions::VERSION_STRING); documentXML.LinkEndChild(mainXML); auto *geometryXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_GEOMETRY); { // begin geometry geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XX, geometry->GetMatrixColumn(0)[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XY, geometry->GetMatrixColumn(0)[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_XZ, geometry->GetMatrixColumn(0)[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YX, geometry->GetMatrixColumn(1)[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YY, geometry->GetMatrixColumn(1)[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_YZ, geometry->GetMatrixColumn(1)[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZX, geometry->GetMatrixColumn(2)[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZY, geometry->GetMatrixColumn(2)[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_MATRIX_ZZ, geometry->GetMatrixColumn(2)[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_X, geometry->GetOrigin()[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Y, geometry->GetOrigin()[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ORIGIN_Z, geometry->GetOrigin()[2]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_SPACING_X, geometry->GetSpacing()[0]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Y, geometry->GetSpacing()[1]); geometryXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_SPACING_Z, geometry->GetSpacing()[2]); } // end geometry mainXML->LinkEndChild(geometryXML); auto *verticesXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_VERTICES); { // begin vertices section std::vector vertexVector = tubeGraph->GetVectorOfAllVertices(); for (unsigned int index = 0; index < vertexVector.size(); index++) { auto *vertexXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_VERTEX); vertexXML->SetAttribute(mitk::TubeGraphDefinitions::XML_VERTEX_ID, tubeGraph->GetVertexDescriptor(vertexVector[index])); // element of each vertex const mitk::TubeElement *element = vertexVector[index].GetTubeElement(); auto *elementXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ELEMENT); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, element->GetCoordinates().GetElement(0)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, element->GetCoordinates().GetElement(1)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, element->GetCoordinates().GetElement(2)); if (dynamic_cast(element)) elementXML->SetDoubleAttribute( mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, (dynamic_cast(element))->GetDiameter()); else elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, 2); vertexXML->LinkEndChild(elementXML); verticesXML->LinkEndChild(vertexXML); } } // end vertices section mainXML->LinkEndChild(verticesXML); auto *edgesXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_EDGES); { // begin edges section std::vector edgeVector = tubeGraph->GetVectorOfAllEdges(); for (unsigned int index = 0; index < edgeVector.size(); index++) { auto *edgeXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_EDGE); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_ID, index); std::pair soureTargetPair = tubeGraph->GetVerticesOfAnEdge(tubeGraph->GetEdgeDescriptor(edgeVector[index])); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_SOURCE_ID, tubeGraph->GetVertexDescriptor(soureTargetPair.first)); edgeXML->SetAttribute(mitk::TubeGraphDefinitions::XML_EDGE_TARGET_ID, tubeGraph->GetVertexDescriptor(soureTargetPair.second)); // begin elements of the edge std::vector elementVector = edgeVector[index].GetElementVector(); for (unsigned int elementIndex = 0; elementIndex < elementVector.size(); elementIndex++) { auto *elementXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ELEMENT); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_X, elementVector[elementIndex]->GetCoordinates().GetElement(0)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Y, elementVector[elementIndex]->GetCoordinates().GetElement(1)); elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_Z, elementVector[elementIndex]->GetCoordinates().GetElement(2)); if (dynamic_cast(elementVector[elementIndex])) elementXML->SetDoubleAttribute( mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, (dynamic_cast(elementVector[elementIndex]))->GetDiameter()); else elementXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_ELEMENT_DIAMETER, 2); edgeXML->LinkEndChild(elementXML); // elementsXML->LinkEndChild(elementXML); } edgesXML->LinkEndChild(edgeXML); } } // end edges section mainXML->LinkEndChild(edgesXML); auto *labelGroupsXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_LABELGROUPS); { // begin label group section std::vector labelGroupVector = tubeGraphProperty->GetLabelGroups(); for (unsigned int index = 0; index < labelGroupVector.size(); index++) { auto *labelGroupXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_LABELGROUP); labelGroupXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME, labelGroupVector[index]->labelGroupName); // begin labels of the label group std::vector labelVector = labelGroupVector[index]->labels; for (unsigned int labelIndex = 0; labelIndex < labelVector.size(); labelIndex++) { auto *labelXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_LABEL); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_NAME, labelVector[labelIndex]->labelName); labelXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_VISIBILITY, labelVector[labelIndex]->isVisible); labelXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_R, labelVector[labelIndex]->labelColor[0]); labelXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_G, labelVector[labelIndex]->labelColor[1]); labelXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_LABEL_COLOR_B, labelVector[labelIndex]->labelColor[2]); labelGroupXML->LinkEndChild(labelXML); } labelGroupsXML->LinkEndChild(labelGroupXML); } } // end labe group section mainXML->LinkEndChild(labelGroupsXML); auto *attributionsXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTIONS); { // begin attributions section std::map tubeToLabelGroup = tubeGraphProperty->GetTubesToLabels(); for (auto it = tubeToLabelGroup.begin(); it != tubeToLabelGroup.end(); it++) { auto *attributXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ATTRIBUTION); attributXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, it->first.first.first); attributXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, it->first.first.second); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABELGROUP_NAME, it->first.second); attributXML->SetAttribute(mitk::TubeGraphDefinitions::XML_LABEL_NAME, it->second); attributionsXML->LinkEndChild(attributXML); } } // end attributions section mainXML->LinkEndChild(attributionsXML); auto *annotationsXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ANNOTATIONS); { // begin annotations section std::vector annotations = tubeGraphProperty->GetAnnotations(); for (unsigned int index = 0; index < annotations.size(); index++) { auto *annotationXML = new TiXmlElement(mitk::TubeGraphDefinitions::XML_ANNOTATION); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_NAME, annotations[index]->name); annotationXML->SetAttribute(mitk::TubeGraphDefinitions::XML_ANNOTATION_DESCRIPTION, annotations[index]->description); annotationXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_1, annotations[index]->tube.first); annotationXML->SetDoubleAttribute(mitk::TubeGraphDefinitions::XML_TUBE_ID_2, annotations[index]->tube.second); annotationsXML->LinkEndChild(annotationXML); } } // end annotations section mainXML->LinkEndChild(annotationsXML); } // end document TiXmlPrinter printer; printer.SetStreamPrinting(); documentXML.Accept(&printer); out << printer.Str(); } AbstractFileIO::ConfidenceLevel TubeGraphIO::GetWriterConfidenceLevel() const { if (AbstractFileIO::GetWriterConfidenceLevel() == Unsupported) return Unsupported; return Supported; } TubeGraphIO *TubeGraphIO::IOClone() const { return new TubeGraphIO(*this); } } const mitk::BoundingBox::Pointer mitk::TubeGraphIO::ComputeBoundingBox(mitk::TubeGraph::Pointer graph) const { BoundingBox::Pointer boundingBox = BoundingBox::New(); BoundingBox::PointIdentifier pointid = 0; BoundingBox::PointsContainer::Pointer pointscontainer = BoundingBox::PointsContainer::New(); ScalarType nullpoint[] = {0, 0, 0}; BoundingBox::PointType p(nullpoint); // traverse the tree and add each point to the pointscontainer mitk::Point3D pos; std::vector vertexVector = graph->GetVectorOfAllVertices(); for (auto vertex = vertexVector.begin(); vertex != vertexVector.end(); ++vertex) { pos = vertex->GetTubeElement()->GetCoordinates(); p[0] = pos[0]; p[1] = pos[1]; p[2] = pos[2]; pointscontainer->InsertElement(pointid++, p); } std::vector edgeVector = graph->GetVectorOfAllEdges(); for (auto edge = edgeVector.begin(); edge != edgeVector.end(); ++edge) { std::vector allElements = edge->GetElementVector(); for (unsigned int index = 0; index < edge->GetNumberOfElements(); index++) { pos = allElements[index]->GetCoordinates(); p[0] = pos[0]; p[1] = pos[1]; p[2] = pos[2]; pointscontainer->InsertElement(pointid++, p); } } boundingBox->SetPoints(pointscontainer); boundingBox->ComputeBoundingBox(); return boundingBox; } diff --git a/Modules/US/USModel/mitkUSDeviceReaderXML.cpp b/Modules/US/USModel/mitkUSDeviceReaderXML.cpp index 4295678f0e..250b7af835 100644 --- a/Modules/US/USModel/mitkUSDeviceReaderXML.cpp +++ b/Modules/US/USModel/mitkUSDeviceReaderXML.cpp @@ -1,204 +1,204 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ // MITK #include "mitkUSDeviceReaderWriterConstants.h" #include "mitkUSDeviceReaderXML.h" #include #include #include // Third Party #include #include #include mitk::USDeviceReaderXML::USDeviceReaderXML() : AbstractFileReader( mitk::IGTMimeTypes::USDEVICEINFORMATIONXML_MIMETYPE(), "MITK USDevice Reader (XML)"), m_Filename("") { RegisterService(); } mitk::USDeviceReaderXML::~USDeviceReaderXML() { } mitk::USDeviceReaderXML::USDeviceConfigData &mitk::USDeviceReaderXML::GetUSDeviceConfigData() { return m_DeviceConfig; } mitk::USDeviceReaderXML::USDeviceReaderXML(const mitk::USDeviceReaderXML& other) : AbstractFileReader(other) { } mitk::USDeviceReaderXML* mitk::USDeviceReaderXML::Clone() const { return new USDeviceReaderXML(*this); } -std::vector> mitk::USDeviceReaderXML::Read() +std::vector> mitk::USDeviceReaderXML::DoRead() { MITK_WARN << "This method is not implemented. \ Please use the method ReadUltrasoundDeviceConfiguration() instead."; std::vector result; return result; } bool mitk::USDeviceReaderXML::ReadUltrasoundDeviceConfiguration() { MITK_INFO << "Try to start reading xml device configuration..."; if (m_Filename == "") { MITK_WARN << "Cannot read file - empty filename!"; return false; } TiXmlDocument document(m_Filename); if (!document.LoadFile()) { MITK_ERROR << "Error when opening and reading file :" << m_Filename; return false; } TiXmlHandle documentHandle(&document); TiXmlElement* ultrasoundDeviceTag = documentHandle.FirstChildElement(TAG_ULTRASOUNDDEVICE).ToElement(); if (ultrasoundDeviceTag == nullptr) { MITK_ERROR << "Error parsing the file :" << m_Filename << std::endl << "Wrong xml format structure."; return false; } //Extract attribute information of the ULTRASOUNDDEVICE-Tag: this->ExtractAttributeInformationOfUltrasoundDeviceTag(ultrasoundDeviceTag); TiXmlElement* generalSettingsTag = documentHandle.FirstChildElement(TAG_ULTRASOUNDDEVICE).FirstChildElement(TAG_GENERALSETTINGS).ToElement(); if (generalSettingsTag == nullptr) { MITK_ERROR << "Error parsing the GENERALSETTINGS-Tag in the file :" << m_Filename; return false; } //Extract attribute information of the GENERALSETTINGS-Tag: this->ExtractAttributeInformationOfGeneralSettingsTag(generalSettingsTag); TiXmlElement* probesTag = documentHandle.FirstChildElement(TAG_ULTRASOUNDDEVICE).FirstChildElement(TAG_PROBES).ToElement(); if (probesTag == nullptr) { MITK_ERROR << "Error: PROBES-Tag was not found in the file :" << m_Filename << "Therefore, creating default probe."; //Create default ultrasound probe: mitk::USProbe::Pointer ultrasoundProbeDefault = mitk::USProbe::New(); ultrasoundProbeDefault->SetName("default"); ultrasoundProbeDefault->SetDepth(0); m_DeviceConfig.probes.push_back(ultrasoundProbeDefault); return true; } //Extract all saved and configured probes of the USDevice: for (TiXmlElement* probeTag = probesTag->FirstChildElement(TAG_PROBE); probeTag != nullptr; probeTag = probeTag->NextSiblingElement()) { this->ExtractProbe(probeTag); } return true; } void mitk::USDeviceReaderXML::SetFilename(std::string filename) { m_Filename = filename; } void mitk::USDeviceReaderXML::ExtractAttributeInformationOfUltrasoundDeviceTag(TiXmlElement *ultrasoundTag) { ultrasoundTag->QueryDoubleAttribute(ATTR_FILEVERS, &m_DeviceConfig.fileversion); ultrasoundTag->QueryStringAttribute(ATTR_TYPE, &m_DeviceConfig.deviceType); ultrasoundTag->QueryStringAttribute(ATTR_NAME, &m_DeviceConfig.deviceName); ultrasoundTag->QueryStringAttribute(ATTR_MANUFACTURER, &m_DeviceConfig.manufacturer); ultrasoundTag->QueryStringAttribute(ATTR_MODEL, &m_DeviceConfig.model); ultrasoundTag->QueryStringAttribute(ATTR_COMMENT, &m_DeviceConfig.comment); ultrasoundTag->QueryIntAttribute(ATTR_IMAGESTREAMS, &m_DeviceConfig.numberOfImageStreams); ultrasoundTag->QueryStringAttribute(ATTR_HOST, &m_DeviceConfig.host); ultrasoundTag->QueryIntAttribute(ATTR_PORT, &m_DeviceConfig.port); ultrasoundTag->QueryBoolAttribute(ATTR_SERVER, &m_DeviceConfig.server); } void mitk::USDeviceReaderXML::ExtractAttributeInformationOfGeneralSettingsTag(TiXmlElement *generalSettingsTag) { generalSettingsTag->QueryBoolAttribute(ATTR_GREYSCALE, &m_DeviceConfig.useGreyscale); generalSettingsTag->QueryBoolAttribute(ATTR_RESOLUTIONOVERRIDE, &m_DeviceConfig.useResolutionOverride); generalSettingsTag->QueryIntAttribute(ATTR_RESOLUTIONHEIGHT, &m_DeviceConfig.resolutionHeight); generalSettingsTag->QueryIntAttribute(ATTR_RESOLUTIONWIDTH, &m_DeviceConfig.resolutionWidth); generalSettingsTag->QueryIntAttribute(ATTR_SOURCEID, &m_DeviceConfig.sourceID); generalSettingsTag->QueryStringAttribute(ATTR_FILEPATH, &m_DeviceConfig.filepathVideoSource); generalSettingsTag->QueryIntAttribute(ATTR_OPENCVPORT, &m_DeviceConfig.opencvPort); } void mitk::USDeviceReaderXML::ExtractProbe(TiXmlElement *probeTag) { mitk::USProbe::Pointer ultrasoundProbe = mitk::USProbe::New(); std::string probeName; probeTag->QueryStringAttribute(ATTR_NAME, &probeName); ultrasoundProbe->SetName(probeName); TiXmlElement* depthsTag = probeTag->FirstChildElement(TAG_DEPTHS); if (depthsTag != nullptr) { for (TiXmlElement* depthTag = depthsTag->FirstChildElement(TAG_DEPTH); depthTag != nullptr; depthTag = depthTag->NextSiblingElement()) { int depth = 0; mitk::Vector3D spacing; spacing[0] = 1; spacing[1] = 1; spacing[2] = 1; depthTag->QueryIntAttribute(ATTR_DEPTH, &depth); TiXmlElement* spacingTag = depthTag->FirstChildElement(TAG_SPACING); if (spacingTag != nullptr) { spacingTag->QueryDoubleAttribute(ATTR_X, &spacing[0]); spacingTag->QueryDoubleAttribute(ATTR_Y, &spacing[1]); } ultrasoundProbe->SetDepthAndSpacing(depth, spacing); } } else { MITK_ERROR << "Error: DEPTHS-Tag was not found in the file :" << m_Filename << "Therefore, creating default depth [0] and spacing [1,1,1] for the probe."; ultrasoundProbe->SetDepth(0); } unsigned int croppingTop = 0; unsigned int croppingBottom = 0; unsigned int croppingLeft = 0; unsigned int croppingRight = 0; TiXmlElement* croppingTag = probeTag->FirstChildElement(TAG_CROPPING); if (croppingTag != nullptr) { croppingTag->QueryUnsignedAttribute(ATTR_TOP, &croppingTop); croppingTag->QueryUnsignedAttribute(ATTR_BOTTOM, &croppingBottom); croppingTag->QueryUnsignedAttribute(ATTR_LEFT, &croppingLeft); croppingTag->QueryUnsignedAttribute(ATTR_RIGHT, &croppingRight); } ultrasoundProbe->SetProbeCropping(croppingTop, croppingBottom, croppingLeft, croppingRight); m_DeviceConfig.probes.push_back(ultrasoundProbe); } diff --git a/Modules/US/USModel/mitkUSDeviceReaderXML.h b/Modules/US/USModel/mitkUSDeviceReaderXML.h index 74d3d76b9f..a81663d7c2 100644 --- a/Modules/US/USModel/mitkUSDeviceReaderXML.h +++ b/Modules/US/USModel/mitkUSDeviceReaderXML.h @@ -1,101 +1,103 @@ /*============================================================================ The Medical Imaging Interaction Toolkit (MITK) Copyright (c) German Cancer Research Center (DKFZ) All rights reserved. Use of this source code is governed by a 3-clause BSD license that can be found in the LICENSE file. ============================================================================*/ #ifndef mitkUSDeviceReaderXML_H_HEADER_INCLUDED_ #define mitkUSDeviceReaderXML_H_HEADER_INCLUDED_ #include #include #include class TiXmlElement; class TiXmlNode; namespace mitk { class MITKUS_EXPORT USDeviceReaderXML : public AbstractFileReader { public: USDeviceReaderXML(); ~USDeviceReaderXML() override; using AbstractFileReader::Read; - std::vector> Read() override; + bool ReadUltrasoundDeviceConfiguration(); void SetFilename(std::string filename); typedef struct USDeviceConfigData_ { double fileversion; std::string deviceType; std::string deviceName; std::string manufacturer; std::string model; std::string comment; std::string host; int port; bool server; int numberOfImageStreams; bool useGreyscale; bool useResolutionOverride; int resolutionWidth; int resolutionHeight; int sourceID; std::string filepathVideoSource; int opencvPort; std::vector probes; USDeviceConfigData_() : fileversion(0), deviceType("Unknown"), deviceName("Unknown"), manufacturer("Unknown"), comment(""), host("localhost"), port(18944), server(false), numberOfImageStreams(1), useGreyscale(true), useResolutionOverride(true), resolutionWidth(640), resolutionHeight(480), sourceID(0), filepathVideoSource(""), opencvPort(0) { }; }USDeviceConfigData; USDeviceConfigData &GetUSDeviceConfigData(); protected: + std::vector> DoRead() override; + USDeviceReaderXML(const USDeviceReaderXML& other); mitk::USDeviceReaderXML* Clone() const override; /** * \brief Extracts all stored attribute information of the ULTRASOUNDDEVICE-Tag. */ void ExtractAttributeInformationOfUltrasoundDeviceTag(TiXmlElement *element); /** * \brief Extracts all stored attribute information of the GENERALSETTINGS-Tag. */ void ExtractAttributeInformationOfGeneralSettingsTag(TiXmlElement *element); /** * \brief Extracts all stored information of a single ultrasound probe. */ void ExtractProbe(TiXmlElement *element); private: std::string m_Filename; USDeviceConfigData m_DeviceConfig; }; } // namespace mitk #endif // mitkUSDeviceReaderXML_H_HEADER_INCLUDED_ diff --git a/SuperBuild.cmake b/SuperBuild.cmake index fca770edd6..0f794fd45f 100644 --- a/SuperBuild.cmake +++ b/SuperBuild.cmake @@ -1,504 +1,505 @@ #----------------------------------------------------------------------------- # Convenient macro allowing to download a file #----------------------------------------------------------------------------- if(NOT MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL) set(MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL http://mitk.org/download/thirdparty) endif() 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() #----------------------------------------------------------------------------- # MITK Prerequisites #----------------------------------------------------------------------------- if(UNIX AND NOT APPLE) include(mitkFunctionCheckPackageHeader) # Check for libxt-dev mitkFunctionCheckPackageHeader(StringDefs.h libxt-dev /usr/include/X11/) # Check for libtiff4-dev mitkFunctionCheckPackageHeader(tiff.h libtiff4-dev) endif() # We need a proper patch program. On Linux and MacOS, we assume # that "patch" is available. On Windows, we download patch.exe # if not patch program is found. find_program(PATCH_COMMAND patch) if((NOT PATCH_COMMAND OR NOT EXISTS ${PATCH_COMMAND}) AND WIN32) downloadFile(${MITK_THIRDPARTY_DOWNLOAD_PREFIX_URL}/patch.exe ${CMAKE_CURRENT_BINARY_DIR}/patch.exe) find_program(PATCH_COMMAND patch ${CMAKE_CURRENT_BINARY_DIR}) endif() if(NOT PATCH_COMMAND) message(FATAL_ERROR "No patch program found.") endif() #----------------------------------------------------------------------------- # ExternalProjects #----------------------------------------------------------------------------- get_property(external_projects GLOBAL PROPERTY MITK_EXTERNAL_PROJECTS) if(MITK_CTEST_SCRIPT_MODE) # Write a file containing the list of enabled external project targets. # This file can be read by a ctest script to separately build projects. set(SUPERBUILD_TARGETS ) foreach(proj ${external_projects}) if(MITK_USE_${proj}) list(APPEND SUPERBUILD_TARGETS ${proj}) endif() endforeach() file(WRITE "${CMAKE_BINARY_DIR}/SuperBuildTargets.cmake" "set(SUPERBUILD_TARGETS ${SUPERBUILD_TARGETS})") endif() # A list of "nice" external projects, playing well together with CMake set(nice_external_projects ${external_projects}) list(REMOVE_ITEM nice_external_projects Boost) foreach(proj ${nice_external_projects}) if(MITK_USE_${proj}) set(EXTERNAL_${proj}_DIR "${${proj}_DIR}" CACHE PATH "Path to ${proj} build directory") mark_as_advanced(EXTERNAL_${proj}_DIR) if(EXTERNAL_${proj}_DIR) set(${proj}_DIR ${EXTERNAL_${proj}_DIR}) endif() endif() endforeach() set(EXTERNAL_BOOST_ROOT "${BOOST_ROOT}" CACHE PATH "Path to Boost directory") mark_as_advanced(EXTERNAL_BOOST_ROOT) if(EXTERNAL_BOOST_ROOT) set(BOOST_ROOT ${EXTERNAL_BOOST_ROOT}) endif() # Setup file for setting custom ctest vars configure_file( CMake/SuperbuildCTestCustom.cmake.in ${MITK_BINARY_DIR}/CTestCustom.cmake @ONLY ) if(BUILD_TESTING) set(EXTERNAL_MITK_DATA_DIR "${MITK_DATA_DIR}" CACHE PATH "Path to the MITK data directory") mark_as_advanced(EXTERNAL_MITK_DATA_DIR) if(EXTERNAL_MITK_DATA_DIR) set(MITK_DATA_DIR ${EXTERNAL_MITK_DATA_DIR}) endif() endif() #----------------------------------------------------------------------------- # External project settings #----------------------------------------------------------------------------- include(ExternalProject) include(mitkMacroQueryCustomEPVars) include(mitkFunctionInstallExternalCMakeProject) include(mitkFunctionCleanExternalProject) option(MITK_AUTOCLEAN_EXTERNAL_PROJECTS "Experimental: Clean external project builds if updated" OFF) mark_as_advanced(MITK_AUTOCLEAN_EXTERNAL_PROJECTS) set(ep_prefix "${CMAKE_BINARY_DIR}/ep") set_property(DIRECTORY PROPERTY EP_PREFIX ${ep_prefix}) # Compute -G arg for configuring external projects with the same CMake generator: if(CMAKE_EXTRA_GENERATOR) set(gen "${CMAKE_EXTRA_GENERATOR} - ${CMAKE_GENERATOR}") else() set(gen "${CMAKE_GENERATOR}") endif() set(gen_platform ${CMAKE_GENERATOR_PLATFORM}) # Use this value where semi-colons are needed in ep_add args: set(sep "^^") ## if(MSVC_VERSION) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} /bigobj /MP") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /bigobj /MP") endif() if(MITK_USE_Boost_LIBRARIES) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DBOOST_ALL_DYN_LINK") endif() # This is a workaround for passing linker flags # actually down to the linker invocation set(_cmake_required_flags_orig ${CMAKE_REQUIRED_FLAGS}) set(CMAKE_REQUIRED_FLAGS "-Wl,-rpath") mitkFunctionCheckCompilerFlags(${CMAKE_REQUIRED_FLAGS} _has_rpath_flag) set(CMAKE_REQUIRED_FLAGS ${_cmake_required_flags_orig}) set(_install_rpath_linkflag ) if(_has_rpath_flag) if(APPLE) set(_install_rpath_linkflag "-Wl,-rpath,@loader_path/../lib") else() set(_install_rpath_linkflag "-Wl,-rpath='$ORIGIN/../lib'") endif() endif() set(_install_rpath) if(APPLE) set(_install_rpath "@loader_path/../lib") elseif(UNIX) # this work for libraries as well as executables set(_install_rpath "\$ORIGIN/../lib") endif() set(ep_common_args -DCMAKE_CXX_EXTENSIONS:STRING=${CMAKE_CXX_EXTENSIONS} -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED:BOOL=${CMAKE_CXX_STANDARD_REQUIRED} -DCMAKE_MACOSX_RPATH:BOOL=TRUE "-DCMAKE_INSTALL_RPATH:STRING=${_install_rpath}" -DBUILD_TESTING:BOOL=OFF -DCMAKE_INSTALL_PREFIX:PATH= -DBUILD_SHARED_LIBS:BOOL=ON -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} -DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${MITK_CXX14_FLAG}" #debug flags -DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} -DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG} #release flags -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE} -DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE} #relwithdebinfo -DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DCMAKE_C_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO} #link flags -DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS} -DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS} -DCMAKE_MODULE_LINKER_FLAGS:STRING=${CMAKE_MODULE_LINKER_FLAGS} ) if(MSVC_VERSION) list(APPEND ep_common_args -DCMAKE_DEBUG_POSTFIX:STRING=d ) set(DCMTK_CMAKE_DEBUG_POSTFIX d) endif() set(ep_common_cache_args ) set(ep_common_cache_default_args "-DCMAKE_PREFIX_PATH:PATH=;${CMAKE_PREFIX_PATH}" "-DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH}" "-DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH}" ) # Pass the CMAKE_OSX variables to external projects if(APPLE) set(MAC_OSX_ARCHITECTURE_ARGS -DCMAKE_OSX_ARCHITECTURES:PATH=${CMAKE_OSX_ARCHITECTURES} -DCMAKE_OSX_DEPLOYMENT_TARGET:PATH=${CMAKE_OSX_DEPLOYMENT_TARGET} -DCMAKE_OSX_SYSROOT:PATH=${CMAKE_OSX_SYSROOT} ) set(ep_common_args ${MAC_OSX_ARCHITECTURE_ARGS} ${ep_common_args} ) endif() set(mitk_superbuild_ep_args) set(mitk_depends ) # Include external projects include(CMakeExternals/MITKData.cmake) foreach(p ${external_projects}) set(p_hash "") set(p_file "${CMAKE_SOURCE_DIR}/CMakeExternals/${p}.cmake") if(EXISTS ${p_file}) file(MD5 ${p_file} p_hash) else() foreach(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIRS}) get_filename_component(MITK_EXTENSION_DIR ${MITK_EXTENSION_DIR} ABSOLUTE) set(MITK_CMAKE_EXTERNALS_EXTENSION_DIR ${MITK_EXTENSION_DIR}/CMakeExternals) set(p_file "${MITK_CMAKE_EXTERNALS_EXTENSION_DIR}/${p}.cmake") if(EXISTS ${p_file}) file(MD5 ${p_file} p_hash) break() endif() endforeach() endif() if(p_hash) set(p_hash_file "${ep_prefix}/tmp/${p}-hash.txt") if(MITK_AUTOCLEAN_EXTERNAL_PROJECTS) if(EXISTS ${p_hash_file}) file(READ ${p_hash_file} p_prev_hash) if(NOT p_hash STREQUAL p_prev_hash) mitkCleanExternalProject(${p}) endif() endif() endif() file(WRITE ${p_hash_file} ${p_hash}) endif() include(${p_file} OPTIONAL) list(APPEND mitk_superbuild_ep_args -DMITK_USE_${p}:BOOL=${MITK_USE_${p}} ) get_property(_package GLOBAL PROPERTY MITK_${p}_PACKAGE) if(_package) list(APPEND mitk_superbuild_ep_args -D${p}_DIR:PATH=${${p}_DIR}) endif() list(APPEND mitk_depends ${${p}_DEPENDS}) endforeach() if (SWIG_EXECUTABLE) list(APPEND mitk_superbuild_ep_args -DSWIG_EXECUTABLE=${SWIG_EXECUTABLE}) endif() #----------------------------------------------------------------------------- # Set superbuild boolean args #----------------------------------------------------------------------------- set(mitk_cmake_boolean_args BUILD_SHARED_LIBS WITH_COVERAGE BUILD_TESTING MITK_BUILD_ALL_PLUGINS MITK_BUILD_ALL_APPS MITK_BUILD_EXAMPLES MITK_USE_Qt5 MITK_USE_SYSTEM_Boost MITK_USE_BLUEBERRY MITK_USE_OpenCL MITK_USE_OpenMP MITK_ENABLE_PIC_READER ) #----------------------------------------------------------------------------- # Create the final variable containing superbuild boolean args #----------------------------------------------------------------------------- set(mitk_superbuild_boolean_args) foreach(mitk_cmake_arg ${mitk_cmake_boolean_args}) list(APPEND mitk_superbuild_boolean_args -D${mitk_cmake_arg}:BOOL=${${mitk_cmake_arg}}) endforeach() if(MITK_BUILD_ALL_PLUGINS) list(APPEND mitk_superbuild_boolean_args -DBLUEBERRY_BUILD_ALL_PLUGINS:BOOL=ON) endif() #----------------------------------------------------------------------------- # MITK Utilities #----------------------------------------------------------------------------- set(proj MITK-Utilities) ExternalProject_Add(${proj} DOWNLOAD_COMMAND "" CONFIGURE_COMMAND "" BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS ${mitk_depends} ) #----------------------------------------------------------------------------- # Additional MITK CXX/C Flags #----------------------------------------------------------------------------- set(MITK_ADDITIONAL_C_FLAGS "" CACHE STRING "Additional C Flags for MITK") set(MITK_ADDITIONAL_C_FLAGS_RELEASE "" CACHE STRING "Additional Release C Flags for MITK") set(MITK_ADDITIONAL_C_FLAGS_DEBUG "" CACHE STRING "Additional Debug C Flags for MITK") mark_as_advanced(MITK_ADDITIONAL_C_FLAGS MITK_ADDITIONAL_C_FLAGS_DEBUG MITK_ADDITIONAL_C_FLAGS_RELEASE) set(MITK_ADDITIONAL_CXX_FLAGS "" CACHE STRING "Additional CXX Flags for MITK") set(MITK_ADDITIONAL_CXX_FLAGS_RELEASE "" CACHE STRING "Additional Release CXX Flags for MITK") set(MITK_ADDITIONAL_CXX_FLAGS_DEBUG "" CACHE STRING "Additional Debug CXX Flags for MITK") mark_as_advanced(MITK_ADDITIONAL_CXX_FLAGS MITK_ADDITIONAL_CXX_FLAGS_DEBUG MITK_ADDITIONAL_CXX_FLAGS_RELEASE) set(MITK_ADDITIONAL_EXE_LINKER_FLAGS "" CACHE STRING "Additional exe linker flags for MITK") set(MITK_ADDITIONAL_SHARED_LINKER_FLAGS "" CACHE STRING "Additional shared linker flags for MITK") set(MITK_ADDITIONAL_MODULE_LINKER_FLAGS "" CACHE STRING "Additional module linker flags for MITK") mark_as_advanced(MITK_ADDITIONAL_EXE_LINKER_FLAGS MITK_ADDITIONAL_SHARED_LINKER_FLAGS MITK_ADDITIONAL_MODULE_LINKER_FLAGS) #----------------------------------------------------------------------------- # MITK Configure #----------------------------------------------------------------------------- if(MITK_INITIAL_CACHE_FILE) set(mitk_initial_cache_arg -C "${MITK_INITIAL_CACHE_FILE}") endif() set(mitk_optional_cache_args ) foreach(type RUNTIME ARCHIVE LIBRARY) if(DEFINED CTK_PLUGIN_${type}_OUTPUT_DIRECTORY) list(APPEND mitk_optional_cache_args -DCTK_PLUGIN_${type}_OUTPUT_DIRECTORY:PATH=${CTK_PLUGIN_${type}_OUTPUT_DIRECTORY}) endif() endforeach() # Optional python variables if(MITK_USE_Python3) list(APPEND mitk_optional_cache_args -DMITK_USE_Python3:BOOL=${MITK_USE_Python3} "-DPython3_EXECUTABLE:FILEPATH=${Python3_EXECUTABLE}" "-DPython3_INCLUDE_DIR:PATH=${Python3_INCLUDE_DIRS}" "-DPython3_LIBRARY:FILEPATH=${Python3_LIBRARY}" "-DPython3_STDLIB:FILEPATH=${Python3_STDLIB}" "-DPython3_SITELIB:FILEPATH=${Python3_SITELIB}" ) endif() if(OPENSSL_ROOT_DIR) list(APPEND mitk_optional_cache_args "-DOPENSSL_ROOT_DIR:PATH=${OPENSSL_ROOT_DIR}" ) endif() if(CMAKE_FRAMEWORK_PATH) list(APPEND mitk_optional_cache_args "-DCMAKE_FRAMEWORK_PATH:PATH=${CMAKE_FRAMEWORK_PATH}" ) endif() if(Eigen_INCLUDE_DIR) list(APPEND mitk_optional_cache_args -DEigen_INCLUDE_DIR:PATH=${Eigen_INCLUDE_DIR} ) endif() # Optional pass through of Doxygen if(DOXYGEN_EXECUTABLE) list(APPEND mitk_optional_cache_args -DDOXYGEN_EXECUTABLE:FILEPATH=${DOXYGEN_EXECUTABLE} ) endif() set(proj MITK-Configure) ExternalProject_Add(${proj} LIST_SEPARATOR ${sep} DOWNLOAD_COMMAND "" CMAKE_GENERATOR ${gen} CMAKE_GENERATOR_PLATFORM ${gen_platform} CMAKE_CACHE_ARGS # --------------- Build options ---------------- -DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_INSTALL_PREFIX} -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} "-DCMAKE_PREFIX_PATH:PATH=${ep_prefix};${CMAKE_PREFIX_PATH}" "-DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH}" "-DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH}" # --------------- Compile options ---------------- -DCMAKE_CXX_EXTENSIONS:STRING=${CMAKE_CXX_EXTENSIONS} -DCMAKE_CXX_STANDARD:STRING=${CMAKE_CXX_STANDARD} -DCMAKE_CXX_STANDARD_REQUIRED:BOOL=${CMAKE_CXX_STANDARD_REQUIRED} -DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER} -DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER} "-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS} ${MITK_ADDITIONAL_C_FLAGS}" "-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS} ${MITK_ADDITIONAL_CXX_FLAGS}" # debug flags "-DCMAKE_CXX_FLAGS_DEBUG:STRING=${CMAKE_CXX_FLAGS_DEBUG} ${MITK_ADDITIONAL_CXX_FLAGS_DEBUG}" "-DCMAKE_C_FLAGS_DEBUG:STRING=${CMAKE_C_FLAGS_DEBUG} ${MITK_ADDITIONAL_C_FLAGS_DEBUG}" # release flags "-DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELEASE} ${MITK_ADDITIONAL_CXX_FLAGS_RELEASE}" "-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELEASE} ${MITK_ADDITIONAL_C_FLAGS_RELEASE}" # relwithdebinfo -DCMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -DCMAKE_C_FLAGS_RELWITHDEBINFO:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO} # link flags "-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS} ${MITK_ADDITIONAL_EXE_LINKER_FLAGS}" "-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS} ${MITK_ADDITIONAL_SHARED_LINKER_FLAGS}" "-DCMAKE_MODULE_LINKER_FLAGS:STRING=${CMAKE_MODULE_LINKER_FLAGS} ${MITK_ADDITIONAL_MODULE_LINKER_FLAGS}" # Output directories -DMITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_LIBRARY_OUTPUT_DIRECTORY} -DMITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_RUNTIME_OUTPUT_DIRECTORY} -DMITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY:PATH=${MITK_CMAKE_ARCHIVE_OUTPUT_DIRECTORY} # ------------- Boolean build options -------------- ${mitk_superbuild_boolean_args} ${mitk_optional_cache_args} -DMITK_USE_SUPERBUILD:BOOL=OFF -DMITK_BUILD_CONFIGURATION:STRING=${MITK_BUILD_CONFIGURATION} -DMITK_FAST_TESTING:BOOL=${MITK_FAST_TESTING} -DMITK_XVFB_TESTING:BOOL=${MITK_XVFB_TESTING} + -DMITK_XVFB_TESTING_COMMAND:BOOL=${MITK_XVFB_TESTING_COMMAND} -DCTEST_USE_LAUNCHERS:BOOL=${CTEST_USE_LAUNCHERS} # ----------------- Miscellaneous --------------- -DCMAKE_LIBRARY_PATH:PATH=${CMAKE_LIBRARY_PATH} -DCMAKE_INCLUDE_PATH:PATH=${CMAKE_INCLUDE_PATH} -DMITK_CTEST_SCRIPT_MODE:STRING=${MITK_CTEST_SCRIPT_MODE} -DMITK_SUPERBUILD_BINARY_DIR:PATH=${MITK_BINARY_DIR} -DMITK_MODULES_TO_BUILD:INTERNAL=${MITK_MODULES_TO_BUILD} -DMITK_WHITELIST:STRING=${MITK_WHITELIST} -DMITK_WHITELISTS_EXTERNAL_PATH:STRING=${MITK_WHITELISTS_EXTERNAL_PATH} -DMITK_WHITELISTS_INTERNAL_PATH:STRING=${MITK_WHITELISTS_INTERNAL_PATH} -DMITK_EXTENSION_DIRS:STRING=${MITK_EXTENSION_DIRS} -DMITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_INTEGRAL_PIXEL_TYPES} -DMITK_ACCESSBYITK_FLOATING_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_FLOATING_PIXEL_TYPES} -DMITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_COMPOSITE_PIXEL_TYPES} -DMITK_ACCESSBYITK_VECTOR_PIXEL_TYPES:STRING=${MITK_ACCESSBYITK_VECTOR_PIXEL_TYPES} -DMITK_ACCESSBYITK_DIMENSIONS:STRING=${MITK_ACCESSBYITK_DIMENSIONS} -DMITK_CUSTOM_REVISION_DESC:STRING=${MITK_CUSTOM_REVISION_DESC} # --------------- External project options --------------- -DMITK_DATA_DIR:PATH=${MITK_DATA_DIR} -DMITK_EXTERNAL_PROJECT_PREFIX:PATH=${ep_prefix} -DCppMicroServices_DIR:PATH=${CppMicroServices_DIR} -DDCMTK_CMAKE_DEBUG_POSTFIX:STRING=${DCMTK_CMAKE_DEBUG_POSTFIX} -DBOOST_ROOT:PATH=${BOOST_ROOT} -DBOOST_LIBRARYDIR:PATH=${BOOST_LIBRARYDIR} -DMITK_USE_Boost_LIBRARIES:STRING=${MITK_USE_Boost_LIBRARIES} -DQt5_DIR:PATH=${Qt5_DIR} CMAKE_ARGS ${mitk_initial_cache_arg} ${MAC_OSX_ARCHITECTURE_ARGS} ${mitk_superbuild_ep_args} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR} BINARY_DIR ${CMAKE_BINARY_DIR}/MITK-build BUILD_COMMAND "" INSTALL_COMMAND "" DEPENDS MITK-Utilities ) mitkFunctionInstallExternalCMakeProject(${proj}) #----------------------------------------------------------------------------- # MITK #----------------------------------------------------------------------------- if(CMAKE_GENERATOR MATCHES ".*Makefiles.*") set(mitk_build_cmd "$(MAKE)") else() set(mitk_build_cmd ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/MITK-build --config ${CMAKE_CFG_INTDIR}) endif() if(NOT DEFINED SUPERBUILD_EXCLUDE_MITKBUILD_TARGET OR NOT SUPERBUILD_EXCLUDE_MITKBUILD_TARGET) set(MITKBUILD_TARGET_ALL_OPTION "ALL") else() set(MITKBUILD_TARGET_ALL_OPTION "") endif() add_custom_target(MITK-build ${MITKBUILD_TARGET_ALL_OPTION} COMMAND ${mitk_build_cmd} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/MITK-build DEPENDS MITK-Configure ) #----------------------------------------------------------------------------- # Custom target allowing to drive the build of the MITK project itself #----------------------------------------------------------------------------- add_custom_target(MITK COMMAND ${mitk_build_cmd} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/MITK-build )